Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
guided_filter.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2017-2020 Heiko Bauke.
4 Copyright (C) 2019, 2021 luzpaz.
5 Copyright (C) 2020 Hubert Kowalski.
6 Copyright (C) 2020-2021 Pascal Obry.
7 Copyright (C) 2020-2021 Ralf Brown.
8 Copyright (C) 2022 Hanno Schwalm.
9 Copyright (C) 2022 Martin Bařinka.
10 Copyright (C) 2024 Alban Gruin.
11 Copyright (C) 2024 Alynx Zhou.
12 Copyright (C) 2025-2026 Aurélien PIERRE.
13
14 darktable is free software: you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 darktable is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with darktable. If not, see <http://www.gnu.org/licenses/>.
26
27
28 Implementation of the guided image filter as described in
29
30 "Guided Image Filtering" by Kaiming He, Jian Sun, and Xiaoou Tang in
31 K. Daniilidis, P. Maragos, N. Paragios (Eds.): ECCV 2010, Part I,
32 LNCS 6311, pp. 1-14, 2010. Springer-Verlag Berlin Heidelberg 2010
33
34 "Guided Image Filtering" by Kaiming He, Jian Sun, and Xiaoou Tang in
35 IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 35,
36 no. 6, June 2013, 1397-1409
37
38*/
39
40#include "system/macros.h"
41#include "system/openmp.h"
43#include "system/mem_alloc.h"
44#include "system/simd.h"
45#include "common/logging.h"
48#include "pixel/box_filters.h"
49#include "pixel/guided_filter.h"
50#include "common/opencl.h"
51#include <assert.h>
52#include <float.h>
53#include <stdlib.h>
54#include <string.h>
55
56// processing is split into tiles of this size (or three times the filter
57// width, if greater) to keep memory use under control.
58#define GF_TILE_SIZE 512
59
60// some shorthand to make code more legible
61// if we have OpenMP simd enabled, declare a vectorizable for loop;
62// otherwise, just leave it a plain for()
63#if defined(_OPENMP) && defined(OPENMP_SIMD_)
64#define SIMD_FOR \
65 _Pragma("omp simd") \
66 for
67#else
68#define SIMD_FOR for
69#endif
70
71// avoid cluttering the scalar codepath with #ifdefs by hiding the dependency on SSE2
72#if !(defined(__x86_64__) || defined(__i386__))
73# define _mm_prefetch(where,hint)
74#endif
75
76// the filter does internal tiling to keep memory requirements reasonable, so this structure
77// defines the position of the tile being processed
78typedef struct tile
79{
80 int left, right, lower, upper;
82
83typedef struct color_image
84{
85 float *data;
88
89// allocate space for n-component image of size width x height
90static inline __attribute__((always_inline)) int new_color_image(color_image *img, int width, int height, int ch)
91{
93 if(IS_NULL_PTR(img->data)) return 1;
94 img->width = width;
95 img->height = height;
96 img->stride = ch;
97 return 0;
98}
99
100// free space for n-component image
101static inline __attribute__((always_inline)) void free_color_image(color_image *img_p)
102{
104 img_p->data = NULL;
105}
106
107// get a pointer to pixel number 'i' within the image
108static inline float *get_color_pixel(color_image img, size_t i)
109{
110 return img.data + i * img.stride;
111}
112
113
114// apply guided filter to single-component image img using the 3-components image imgg as a guide
115// the filtering applies a monochrome box filter to a total of 13 image channels:
116// 1 monochrome input image
117// 3 color guide image
118// 3 covariance (R, G, B)
119// 6 variance (R-R, R-G, R-B, G-G, G-B, B-B)
120// for computational efficiency, we'll pack them into a four-channel image and a 9-channel image
121// image instead of running 13 separate box filters: guide+input, R/G/B/R-R/R-G/R-B/G-G/G-B/B-B.
123static int guided_filter_tiling(color_image imgg, gray_image img, gray_image img_out, tile target, const int w,
124 const float eps, const float guide_weight, const float min, const float max)
125{
126 const tile source = { max_i(target.left - 2 * w, 0), min_i(target.right + 2 * w, imgg.width),
127 max_i(target.lower - 2 * w, 0), min_i(target.upper + 2 * w, imgg.height) };
128 const int width = source.right - source.left;
129 const int height = source.upper - source.lower;
130 size_t size = (size_t)width * (size_t)height;
131// since we're packing multiple monochrome planes into a color image, define symbolic constants so that
132// we can keep track of which values we're actually using
133#define INP_MEAN 0
134#define GUIDE_MEAN_R 1
135#define GUIDE_MEAN_G 2
136#define GUIDE_MEAN_B 3
137#define COV_R 0
138#define COV_G 1
139#define COV_B 2
140#define VAR_RR 3
141#define VAR_RG 4
142#define VAR_RB 5
143#define VAR_GG 6
144#define VAR_BB 8
145#define VAR_GB 7
146 color_image mean = { 0 };
147 color_image variance = { 0 };
148 if(new_color_image(&mean, width, height, 4) != 0)
149 return 1;
150
151 if(new_color_image(&variance, width, height, 9) != 0)
152 {
153 free_color_image(&mean);
154 return 1;
155 }
156 const size_t img_dimen = mean.width;
157 size_t img_bak_sz;
158 float *img_bak = dt_pixelpipe_cache_alloc_perthread_float(9*img_dimen, &img_bak_sz);
159 if(IS_NULL_PTR(img_bak))
160 {
161 free_color_image(&variance);
162 free_color_image(&mean);
163 return 1;
164 }
165 int err = 0;
167 for(int j_imgg = source.lower; j_imgg < source.upper; j_imgg++)
168 {
169 int j = j_imgg - source.lower;
170 float *const restrict meanpx = mean.data + 4 * j * mean.width;
171 float *const restrict varpx = variance.data + 9 * j * variance.width;
172 for(int i_imgg = source.left; i_imgg < source.right; i_imgg++)
173 {
174 size_t i = i_imgg - source.left;
175 const float *pixel_ = get_color_pixel(imgg, i_imgg + (size_t)j_imgg * imgg.width);
176 dt_aligned_pixel_t pixel =
177 { pixel_[0] * guide_weight, pixel_[1] * guide_weight, pixel_[2] * guide_weight, pixel_[3] * guide_weight };
178 const float input = img.data[i_imgg + (size_t)j_imgg * img.width];
179 meanpx[4*i+INP_MEAN] = input;
180 meanpx[4*i+GUIDE_MEAN_R] = pixel[0];
181 meanpx[4*i+GUIDE_MEAN_G] = pixel[1];
182 meanpx[4*i+GUIDE_MEAN_B] = pixel[2];
183 varpx[9*i+COV_R] = pixel[0] * input;
184 varpx[9*i+COV_G] = pixel[1] * input;
185 varpx[9*i+COV_B] = pixel[2] * input;
186 varpx[9*i+VAR_RR] = pixel[0] * pixel[0];
187 varpx[9*i+VAR_RG] = pixel[0] * pixel[1];
188 varpx[9*i+VAR_RB] = pixel[0] * pixel[2];
189 varpx[9*i+VAR_GG] = pixel[1] * pixel[1];
190 varpx[9*i+VAR_GB] = pixel[1] * pixel[2];
191 varpx[9*i+VAR_BB] = pixel[2] * pixel[2];
192 }
193 // apply horizontal pass of box mean filter while the cache is still hot
194 float *const restrict scratch = dt_get_perthread(img_bak, img_bak_sz);
195 if(IS_NULL_PTR(scratch)
196 || dt_box_mean_horizontal(meanpx, mean.width, 4|BOXFILTER_KAHAN_SUM, w, scratch) != 0
197 || dt_box_mean_horizontal(varpx, variance.width, 9|BOXFILTER_KAHAN_SUM, w, scratch) != 0)
198 {
199#ifdef _OPENMP
200#pragma omp atomic write
201#endif
202 err = 1;
203 }
204 }
206 if(!err && dt_box_mean_vertical(mean.data, mean.height, mean.width, 4|BOXFILTER_KAHAN_SUM, w) != 0)
207 err = 1;
208 if(!err && dt_box_mean_vertical(variance.data, variance.height, variance.width, 9|BOXFILTER_KAHAN_SUM, w) != 0)
209 err = 1;
210
211 if(err)
212 {
213 free_color_image(&variance);
214 free_color_image(&mean);
215 return 1;
216 }
217 // we will recycle memory of 'mean' for the new coefficient arrays a_? and b to reduce memory foot print
218 color_image a_b = mean;
219 #define A_RED 0
220 #define A_GREEN 1
221 #define A_BLUE 2
222 #define B 3
224 for(size_t i = 0; i < size; i++)
225 {
226 const float *meanpx = get_color_pixel(mean, i);
227 const float inp_mean = meanpx[INP_MEAN];
228 const float guide_r = meanpx[GUIDE_MEAN_R];
229 const float guide_g = meanpx[GUIDE_MEAN_G];
230 const float guide_b = meanpx[GUIDE_MEAN_B];
231 float *const varpx = get_color_pixel(variance, i);
232 // solve linear system of equations of size 3x3 via Cramer's rule
233 // symmetric coefficient matrix
234 const float Sigma_0_0 = varpx[VAR_RR] - (guide_r * guide_r) + eps;
235 const float Sigma_0_1 = varpx[VAR_RG] - (guide_r * guide_g);
236 const float Sigma_0_2 = varpx[VAR_RB] - (guide_r * guide_b);
237 const float Sigma_1_1 = varpx[VAR_GG] - (guide_g * guide_g) + eps;;
238 const float Sigma_1_2 = varpx[VAR_GB] - (guide_g * guide_b);
239 const float Sigma_2_2 = varpx[VAR_BB] - (guide_b * guide_b) + eps;
240 const float det0 = Sigma_0_0 * (Sigma_1_1 * Sigma_2_2 - Sigma_1_2 * Sigma_1_2)
241 - Sigma_0_1 * (Sigma_0_1 * Sigma_2_2 - Sigma_0_2 * Sigma_1_2)
242 + Sigma_0_2 * (Sigma_0_1 * Sigma_1_2 - Sigma_0_2 * Sigma_1_1);
243 float a_r_, a_g_, a_b_, b_;
244 if(fabsf(det0) > 4.f * FLT_EPSILON)
245 {
246 const float cov_r = varpx[COV_R] - guide_r * inp_mean;
247 const float cov_g = varpx[COV_G] - guide_g * inp_mean;
248 const float cov_b = varpx[COV_B] - guide_b * inp_mean;
249 const float det1 = cov_r * (Sigma_1_1 * Sigma_2_2 - Sigma_1_2 * Sigma_1_2)
250 - Sigma_0_1 * (cov_g * Sigma_2_2 - cov_b * Sigma_1_2)
251 + Sigma_0_2 * (cov_g * Sigma_1_2 - cov_b * Sigma_1_1);
252 const float det2 = Sigma_0_0 * (cov_g * Sigma_2_2 - cov_b * Sigma_1_2)
253 - cov_r * (Sigma_0_1 * Sigma_2_2 - Sigma_0_2 * Sigma_1_2)
254 + Sigma_0_2 * (Sigma_0_1 * cov_b - Sigma_0_2 * cov_g);
255 const float det3 = Sigma_0_0 * (Sigma_1_1 * cov_b - Sigma_1_2 * cov_g)
256 - Sigma_0_1 * (Sigma_0_1 * cov_b - Sigma_0_2 * cov_g)
257 + cov_r * (Sigma_0_1 * Sigma_1_2 - Sigma_0_2 * Sigma_1_1);
258 a_r_ = det1 / det0;
259 a_g_ = det2 / det0;
260 a_b_ = det3 / det0;
261 b_ = inp_mean - a_r_ * guide_r - a_g_ * guide_g - a_b_ * guide_b;
262 }
263 else
264 {
265 // linear system is singular
266 a_r_ = 0.f;
267 a_g_ = 0.f;
268 a_b_ = 0.f;
269 b_ = get_color_pixel(mean, i)[INP_MEAN];
270 }
271 // now data of imgg_mean_? is no longer needed, we can safely overwrite aliasing arrays
272 a_b.data[4*i+A_RED] = a_r_;
273 a_b.data[4*i+A_GREEN] = a_g_;
274 a_b.data[4*i+A_BLUE] = a_b_;
275 a_b.data[4*i+B] = b_;
276 }
277 free_color_image(&variance);
278
279 if(dt_box_mean(a_b.data, a_b.height, a_b.width, a_b.stride|BOXFILTER_KAHAN_SUM, w, 1))
280 {
281 free_color_image(&mean);
282 return 1;
283 }
285 for(int j_imgg = target.lower; j_imgg < target.upper; j_imgg++)
286 {
287 // index of the left most target pixel in the current row
288 size_t l = target.left + (size_t)j_imgg * imgg.width;
289 // index of the left most source pixel in the current row of the
290 // smaller auxiliary gray-scale images a_r, a_g, a_b, and b
291 // excluding boundary data from neighboring tiles
292 size_t k = (target.left - source.left) + (size_t)(j_imgg - source.lower) * width;
293 for(int i_imgg = target.left; i_imgg < target.right; i_imgg++, k++, l++)
294 {
295 const float *pixel = get_color_pixel(imgg, l);
296 const float *px_ab = get_color_pixel(a_b, k);
297 float res = guide_weight * (px_ab[A_RED] * pixel[0] + px_ab[A_GREEN] * pixel[1] + px_ab[A_BLUE] * pixel[2]);
298 res += px_ab[B];
299 img_out.data[i_imgg + (size_t)j_imgg * imgg.width] = CLAMP(res, min, max);
300 }
301 }
302 free_color_image(&mean);
303 return 0;
304}
305
306static inline __attribute__((always_inline)) int compute_tile_height(const int height, const int w)
307{
308 int tile_h = max_i(3 * w, GF_TILE_SIZE);
309#if 0 // enabling the below doesn't make any measureable speed difference, but does cause a handful of pixels
310 // to round off differently (as does changing GF_TILE_SIZE)
311 if ((height % tile_h) > 0 && (height % tile_h) < GF_TILE_SIZE/3)
312 {
313 // if there's just a sliver left over for the last row of tiles, see whether slicing off a few pixels
314 // gives us a mostly-full tile
315 if (height % (tile_h - 8) >= GF_TILE_SIZE/3)
316 tile_h -= 8;
317 else if (height % (tile_h - w/4) >= GF_TILE_SIZE/3)
318 tile_h -= (w/4);
319 else if (height % (tile_h - w/2) >= GF_TILE_SIZE/3)
320 tile_h -= (w/2);
321 // try adding a few pixels
322 else if (height % (tile_h + 8) >= GF_TILE_SIZE/3)
323 tile_h += 8;
324 else if (height % (tile_h + 16) >= GF_TILE_SIZE/3)
325 tile_h += 16;
326 }
327#endif
328 return tile_h;
329}
330
331static inline __attribute__((always_inline)) int compute_tile_width(const int width, const int w)
332{
333 int tile_w = max_i(3 * w, GF_TILE_SIZE);
334#if 0 // enabling the below doesn't make any measureable speed difference, but does cause a handful of pixels
335 // to round off differently (as does changing GF_TILE_SIZE)
336 if ((width % tile_w) > 0 && (width % tile_w) < GF_TILE_SIZE/2)
337 {
338 // if there's just a sliver left over for the last column of tiles, see whether slicing off a few pixels
339 // gives us a mostly-full tile
340 if (width % (tile_w - 8) >= GF_TILE_SIZE/3)
341 tile_w -= 8;
342 else if (width % (tile_w - w/4) >= GF_TILE_SIZE/3)
343 tile_w -= (w/4);
344 else if (width % (tile_w - w/2) >= GF_TILE_SIZE/3)
345 tile_w -= (w/2);
346 // try adding a few pixels
347 else if (width % (tile_w + 8) >= GF_TILE_SIZE/3)
348 tile_w += 8;
349 else if (width % (tile_w + 16) >= GF_TILE_SIZE/3)
350 tile_w += 16;
351 }
352#endif
353 return tile_w;
354}
355
357int guided_filter(const float *const guide, const float *const in, float *const out, const int width,
358 const int height, const int ch,
359 const int w, // window size
360 const float sqrt_eps, // regularization parameter
361 const float guide_weight, // to balance the amplitudes in the guiding image and the input image
362 const float min, const float max)
363{
364 assert(ch >= 3);
365 assert(w >= 1);
366
367 color_image img_guide = (color_image){ (float *)guide, width, height, ch };
368 gray_image img_in = (gray_image){ (float *)in, width, height };
369 gray_image img_out = (gray_image){ out, width, height };
370 const int tile_width = compute_tile_width(width,w);
371 const int tile_height = compute_tile_height(height,w);
372 const float eps = sqrt_eps * sqrt_eps; // this is the regularization parameter of the original papers
373
374 for(int j = 0; j < height; j += tile_height)
375 {
376 for(int i = 0; i < width; i += tile_width)
377 {
378 tile target = { i, min_i(i + tile_width, width), j, min_i(j + tile_height, height) };
379 if(guided_filter_tiling(img_guide, img_in, img_out, target, w, eps, guide_weight, min, max) != 0)
380 return 1;
381 }
382 }
383 return 0;
384}
385
386#ifdef HAVE_OPENCL
387
388/* The kernels this subsystem compiles, owned HERE. They used to be handed to
389 * common/opencl.c, parked on the application-wide dt_opencl_t, and read back from it --
390 * a round trip through a god-struct that added nothing but an ordering. opencl.c still
391 * calls init/free, because the kernels must be built after the devices exist, but the
392 * pointer never leaves this file. */
394
396{
397 dt_guided_filter_cl_global_t *g = malloc(sizeof(*g));
398 const int program = 26; // guided_filter.cl, from programs.conf
399 g->kernel_guided_filter_split_rgb = dt_opencl_create_kernel(program, "guided_filter_split_rgb_image");
400 g->kernel_guided_filter_box_mean_x = dt_opencl_create_kernel(program, "guided_filter_box_mean_x");
401 g->kernel_guided_filter_box_mean_y = dt_opencl_create_kernel(program, "guided_filter_box_mean_y");
402 g->kernel_guided_filter_guided_filter_covariances
403 = dt_opencl_create_kernel(program, "guided_filter_covariances");
404 g->kernel_guided_filter_guided_filter_variances = dt_opencl_create_kernel(program, "guided_filter_variances");
405 g->kernel_guided_filter_update_covariance = dt_opencl_create_kernel(program, "guided_filter_update_covariance");
406 g->kernel_guided_filter_solve = dt_opencl_create_kernel(program, "guided_filter_solve");
407 g->kernel_guided_filter_generate_result = dt_opencl_create_kernel(program, "guided_filter_generate_result");
409}
410
411
413{
416 if(IS_NULL_PTR(g)) return;
417 // destroy kernels
418 dt_opencl_free_kernel(g->kernel_guided_filter_split_rgb);
419 dt_opencl_free_kernel(g->kernel_guided_filter_box_mean_x);
420 dt_opencl_free_kernel(g->kernel_guided_filter_box_mean_y);
421 dt_opencl_free_kernel(g->kernel_guided_filter_guided_filter_covariances);
422 dt_opencl_free_kernel(g->kernel_guided_filter_guided_filter_variances);
423 dt_opencl_free_kernel(g->kernel_guided_filter_update_covariance);
424 dt_opencl_free_kernel(g->kernel_guided_filter_solve);
425 dt_opencl_free_kernel(g->kernel_guided_filter_generate_result);
426 dt_free(g);
427}
428
429
430static int cl_split_rgb(const int devid, const int width, const int height, cl_mem guide, cl_mem imgg_r,
431 cl_mem imgg_g, cl_mem imgg_b, const float guide_weight)
432{
434 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(int), &width);
435 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), &height);
436 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &guide);
437 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &imgg_r);
438 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &imgg_g);
439 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &imgg_b);
440 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), &guide_weight);
441 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
442 return dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
443}
444
445
446static int cl_box_mean(const int devid, const int width, const int height, const int w, cl_mem in, cl_mem out,
447 cl_mem temp)
448{
450 dt_opencl_set_kernel_arg(devid, kernel_x, 0, sizeof(int), &width);
451 dt_opencl_set_kernel_arg(devid, kernel_x, 1, sizeof(int), &height);
452 dt_opencl_set_kernel_arg(devid, kernel_x, 2, sizeof(cl_mem), &in);
453 dt_opencl_set_kernel_arg(devid, kernel_x, 3, sizeof(cl_mem), &temp);
454 dt_opencl_set_kernel_arg(devid, kernel_x, 4, sizeof(int), &w);
455 const size_t sizes_x[] = { 1, ROUNDUPDHT(height, devid), 1 };
456 const int err = dt_opencl_enqueue_kernel_2d(devid, kernel_x, sizes_x);
457 if(err != CL_SUCCESS) return err;
458
460 dt_opencl_set_kernel_arg(devid, kernel_y, 0, sizeof(int), &width);
461 dt_opencl_set_kernel_arg(devid, kernel_y, 1, sizeof(int), &height);
462 dt_opencl_set_kernel_arg(devid, kernel_y, 2, sizeof(cl_mem), &temp);
463 dt_opencl_set_kernel_arg(devid, kernel_y, 3, sizeof(cl_mem), &out);
464 dt_opencl_set_kernel_arg(devid, kernel_y, 4, sizeof(int), &w);
465 const size_t sizes_y[] = { ROUNDUPDWD(width, devid), 1, 1 };
466 return dt_opencl_enqueue_kernel_2d(devid, kernel_y, sizes_y);
467}
468
469
470static int cl_covariances(const int devid, const int width, const int height, cl_mem guide, cl_mem in,
471 cl_mem cov_imgg_img_r, cl_mem cov_imgg_img_g, cl_mem cov_imgg_img_b,
472 const float guide_weight)
473{
475 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(int), &width);
476 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), &height);
477 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &guide);
478 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &in);
479 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &cov_imgg_img_r);
480 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &cov_imgg_img_g);
481 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &cov_imgg_img_b);
482 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), &guide_weight);
483 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
484 return dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
485}
486
487
488static int cl_variances(const int devid, const int width, const int height, cl_mem guide, cl_mem var_imgg_rr,
489 cl_mem var_imgg_rg, cl_mem var_imgg_rb, cl_mem var_imgg_gg, cl_mem var_imgg_gb,
490 cl_mem var_imgg_bb, const float guide_weight)
491{
493 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(int), &width);
494 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), &height);
495 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &guide);
496 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &var_imgg_rr);
497 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &var_imgg_rg);
498 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &var_imgg_rb);
499 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &var_imgg_gg);
500 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &var_imgg_gb);
501 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), &var_imgg_bb);
502 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(float), &guide_weight);
503 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
504 return dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
505}
506
507
508static int cl_update_covariance(const int devid, const int width, const int height, cl_mem in, cl_mem out,
509 cl_mem a, cl_mem b, float eps)
510{
512 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(int), &width);
513 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), &height);
514 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &in);
515 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &out);
516 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &a);
517 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &b);
518 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), &eps);
519 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
520 return dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
521}
522
523
524static int cl_solve(const int devid, const int width, const int height, cl_mem img_mean, cl_mem imgg_mean_r,
525 cl_mem imgg_mean_g, cl_mem imgg_mean_b, cl_mem cov_imgg_img_r, cl_mem cov_imgg_img_g,
526 cl_mem cov_imgg_img_b, cl_mem var_imgg_rr, cl_mem var_imgg_rg, cl_mem var_imgg_rb,
527 cl_mem var_imgg_gg, cl_mem var_imgg_gb, cl_mem var_imgg_bb, cl_mem a_r, cl_mem a_g, cl_mem a_b,
528 cl_mem b)
529{
531 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(int), &width);
532 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), &height);
533 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &img_mean);
534 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &imgg_mean_r);
535 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &imgg_mean_g);
536 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &imgg_mean_b);
537 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &cov_imgg_img_r);
538 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &cov_imgg_img_g);
539 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(cl_mem), &cov_imgg_img_b);
540 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(cl_mem), &var_imgg_rr);
541 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(cl_mem), &var_imgg_rg);
542 dt_opencl_set_kernel_arg(devid, kernel, 11, sizeof(cl_mem), &var_imgg_rb);
543 dt_opencl_set_kernel_arg(devid, kernel, 12, sizeof(cl_mem), &var_imgg_gg);
544 dt_opencl_set_kernel_arg(devid, kernel, 13, sizeof(cl_mem), &var_imgg_gb);
545 dt_opencl_set_kernel_arg(devid, kernel, 14, sizeof(cl_mem), &var_imgg_bb);
546 dt_opencl_set_kernel_arg(devid, kernel, 15, sizeof(cl_mem), &a_r);
547 dt_opencl_set_kernel_arg(devid, kernel, 16, sizeof(cl_mem), &a_g);
548 dt_opencl_set_kernel_arg(devid, kernel, 17, sizeof(cl_mem), &a_b);
549 dt_opencl_set_kernel_arg(devid, kernel, 18, sizeof(cl_mem), &b);
550 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
551 return dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
552}
553
554
555static int cl_generate_result(const int devid, const int width, const int height, cl_mem guide, cl_mem a_r,
556 cl_mem a_g, cl_mem a_b, cl_mem b, cl_mem out, const float guide_weight,
557 const float min, const float max)
558{
560 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(int), &width);
561 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), &height);
562 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), &guide);
563 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), &a_r);
564 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &a_g);
565 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(cl_mem), &a_b);
566 dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(cl_mem), &b);
567 dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(cl_mem), &out);
568 dt_opencl_set_kernel_arg(devid, kernel, 8, sizeof(float), &guide_weight);
569 dt_opencl_set_kernel_arg(devid, kernel, 9, sizeof(float), &min);
570 dt_opencl_set_kernel_arg(devid, kernel, 10, sizeof(float), &max);
571 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
572 return dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
573}
574
575
576static int guided_filter_cl_impl(int devid, cl_mem guide, cl_mem in, cl_mem out, const int width, const int height,
577 const int ch,
578 const int w, // window size
579 const float sqrt_eps, // regularization parameter
580 const float guide_weight, // to balance the amplitudes in the guiding image and
581 // the input// image
582 const float min, const float max)
583{
584 const float eps = sqrt_eps * sqrt_eps; // this is the regularization parameter of the original papers
585
586 void *temp1 = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
587 void *temp2 = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
588 void *imgg_mean_r = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
589 void *imgg_mean_g = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
590 void *imgg_mean_b = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
591 void *img_mean = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
592 void *cov_imgg_img_r = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
593 void *cov_imgg_img_g = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
594 void *cov_imgg_img_b = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
595 void *var_imgg_rr = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
596 void *var_imgg_gg = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
597 void *var_imgg_bb = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
598 void *var_imgg_rg = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
599 void *var_imgg_rb = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
600 void *var_imgg_gb = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
601 void *a_r = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
602 void *a_g = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
603 void *a_b = dt_opencl_alloc_device(devid, width, height, (int)sizeof(float));
604 void *b = temp2;
605
606 int err = CL_SUCCESS;
607 if(IS_NULL_PTR(temp1) || IS_NULL_PTR(temp2) || //
608 IS_NULL_PTR(imgg_mean_r) || IS_NULL_PTR(imgg_mean_g) || IS_NULL_PTR(imgg_mean_b) || IS_NULL_PTR(img_mean) || //
609 IS_NULL_PTR(cov_imgg_img_r) || IS_NULL_PTR(cov_imgg_img_g) || IS_NULL_PTR(cov_imgg_img_b) || //
610 IS_NULL_PTR(var_imgg_rr) || IS_NULL_PTR(var_imgg_gg) || IS_NULL_PTR(var_imgg_bb) || //
611 IS_NULL_PTR(var_imgg_rg) || IS_NULL_PTR(var_imgg_rb) || IS_NULL_PTR(var_imgg_gb) || //
612 IS_NULL_PTR(a_r) || IS_NULL_PTR(a_g) || IS_NULL_PTR(a_b))
613 {
614 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
615 goto error;
616 }
617
618 err = cl_split_rgb(devid, width, height, guide, imgg_mean_r, imgg_mean_g, imgg_mean_b, guide_weight);
619 if(err != CL_SUCCESS) goto error;
620
621 err = cl_box_mean(devid, width, height, w, in, img_mean, temp1);
622 if(err != CL_SUCCESS) goto error;
623 err = cl_box_mean(devid, width, height, w, imgg_mean_r, imgg_mean_r, temp1);
624 if(err != CL_SUCCESS) goto error;
625 err = cl_box_mean(devid, width, height, w, imgg_mean_g, imgg_mean_g, temp1);
626 if(err != CL_SUCCESS) goto error;
627 err = cl_box_mean(devid, width, height, w, imgg_mean_b, imgg_mean_b, temp1);
628 if(err != CL_SUCCESS) goto error;
629
630 err = cl_covariances(devid, width, height, guide, in, cov_imgg_img_r, cov_imgg_img_g, cov_imgg_img_b,
631 guide_weight);
632 if(err != CL_SUCCESS) goto error;
633
634 err = cl_variances(devid, width, height, guide, var_imgg_rr, var_imgg_rg, var_imgg_rb, var_imgg_gg, var_imgg_gb,
635 var_imgg_bb, guide_weight);
636 if(err != CL_SUCCESS) goto error;
637
638 err = cl_box_mean(devid, width, height, w, cov_imgg_img_r, temp2, temp1);
639 if(err != CL_SUCCESS) goto error;
640 err = cl_update_covariance(devid, width, height, temp2, cov_imgg_img_r, imgg_mean_r, img_mean, 0.f);
641 if(err != CL_SUCCESS) goto error;
642 err = cl_box_mean(devid, width, height, w, cov_imgg_img_g, temp2, temp1);
643 if(err != CL_SUCCESS) goto error;
644 err = cl_update_covariance(devid, width, height, temp2, cov_imgg_img_g, imgg_mean_g, img_mean, 0.f);
645 if(err != CL_SUCCESS) goto error;
646 err = cl_box_mean(devid, width, height, w, cov_imgg_img_b, temp2, temp1);
647 if(err != CL_SUCCESS) goto error;
648 err = cl_update_covariance(devid, width, height, temp2, cov_imgg_img_b, imgg_mean_b, img_mean, 0.f);
649 if(err != CL_SUCCESS) goto error;
650 err = cl_box_mean(devid, width, height, w, var_imgg_rr, temp2, temp1);
651 if(err != CL_SUCCESS) goto error;
652 err = cl_update_covariance(devid, width, height, temp2, var_imgg_rr, imgg_mean_r, imgg_mean_r, eps);
653 if(err != CL_SUCCESS) goto error;
654 err = cl_box_mean(devid, width, height, w, var_imgg_rg, temp2, temp1);
655 if(err != CL_SUCCESS) goto error;
656 err = cl_update_covariance(devid, width, height, temp2, var_imgg_rg, imgg_mean_r, imgg_mean_g, 0.f);
657 if(err != CL_SUCCESS) goto error;
658 err = cl_box_mean(devid, width, height, w, var_imgg_rb, temp2, temp1);
659 if(err != CL_SUCCESS) goto error;
660 err = cl_update_covariance(devid, width, height, temp2, var_imgg_rb, imgg_mean_r, imgg_mean_b, 0.f);
661 if(err != CL_SUCCESS) goto error;
662 err = cl_box_mean(devid, width, height, w, var_imgg_gg, temp2, temp1);
663 if(err != CL_SUCCESS) goto error;
664 err = cl_update_covariance(devid, width, height, temp2, var_imgg_gg, imgg_mean_g, imgg_mean_g, eps);
665 if(err != CL_SUCCESS) goto error;
666 err = cl_box_mean(devid, width, height, w, var_imgg_gb, temp2, temp1);
667 if(err != CL_SUCCESS) goto error;
668 err = cl_update_covariance(devid, width, height, temp2, var_imgg_gb, imgg_mean_g, imgg_mean_b, 0.f);
669 if(err != CL_SUCCESS) goto error;
670 err = cl_box_mean(devid, width, height, w, var_imgg_bb, temp2, temp1);
671 if(err != CL_SUCCESS) goto error;
672 err = cl_update_covariance(devid, width, height, temp2, var_imgg_bb, imgg_mean_b, imgg_mean_b, eps);
673 if(err != CL_SUCCESS) goto error;
674
675 err = cl_solve(devid, width, height, img_mean, imgg_mean_r, imgg_mean_g, imgg_mean_b, cov_imgg_img_r,
676 cov_imgg_img_g, cov_imgg_img_b, var_imgg_rr, var_imgg_rg, var_imgg_rb, var_imgg_gg, var_imgg_gb,
677 var_imgg_bb, a_r, a_g, a_b, b);
678 if(err != CL_SUCCESS) goto error;
679
680 err = cl_box_mean(devid, width, height, w, a_r, a_r, temp1);
681 if(err != CL_SUCCESS) goto error;
682 err = cl_box_mean(devid, width, height, w, a_g, a_g, temp1);
683 if(err != CL_SUCCESS) goto error;
684 err = cl_box_mean(devid, width, height, w, a_b, a_b, temp1);
685 if(err != CL_SUCCESS) goto error;
686 err = cl_box_mean(devid, width, height, w, b, b, temp1);
687 if(err != CL_SUCCESS) goto error;
688
689 err = cl_generate_result(devid, width, height, guide, a_r, a_g, a_b, b, out, guide_weight, min, max);
690
691error:
695 dt_opencl_release_mem_object(var_imgg_rr);
696 dt_opencl_release_mem_object(var_imgg_rg);
697 dt_opencl_release_mem_object(var_imgg_rb);
698 dt_opencl_release_mem_object(var_imgg_gg);
699 dt_opencl_release_mem_object(var_imgg_gb);
700 dt_opencl_release_mem_object(var_imgg_bb);
701 dt_opencl_release_mem_object(cov_imgg_img_r);
702 dt_opencl_release_mem_object(cov_imgg_img_g);
703 dt_opencl_release_mem_object(cov_imgg_img_b);
705 dt_opencl_release_mem_object(imgg_mean_r);
706 dt_opencl_release_mem_object(imgg_mean_g);
707 dt_opencl_release_mem_object(imgg_mean_b);
710
711 return err;
712}
713
714
715static int guided_filter_cl_fallback(int devid, cl_mem guide, cl_mem in, cl_mem out, const int width,
716 const int height, const int ch,
717 const int w, // window size
718 const float sqrt_eps, // regularization parameter
719 const float guide_weight, // to balance the amplitudes in the guiding image
720 // and the input// image
721 const float min, const float max)
722{
723 // fall-back implementation: copy data from device memory to host memory and perform filter
724 // by CPU until there is a proper OpenCL implementation
726 width * height * ch,
727 0);
729 width * height,
730 0);
732 width * height,
733 0);
734 if(!guide_host || IS_NULL_PTR(in_host) || IS_NULL_PTR(out_host))
735 {
739 return 1;
740 }
741 int err;
742 err = dt_opencl_read_host_from_device(devid, guide_host, guide, width, height, ch * sizeof(float));
743 if(err != CL_SUCCESS) goto error;
744 err = dt_opencl_read_host_from_device(devid, in_host, in, width, height, sizeof(float));
745 if(err != CL_SUCCESS) goto error;
746 if(guided_filter(guide_host, in_host, out_host, width, height, ch, w, sqrt_eps, guide_weight, min, max) != 0)
747 {
748 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
749 goto error;
750 }
751 err = dt_opencl_write_host_to_device(devid, out_host, out, width, height, sizeof(float));
752 if(err != CL_SUCCESS) goto error;
753error:
757 return err == CL_SUCCESS ? 0 : 1;
758}
759
760
761int guided_filter_cl(int devid, cl_mem guide, cl_mem in, cl_mem out, const int width, const int height,
762 const int ch,
763 const int w, // window size
764 const float sqrt_eps, // regularization parameter
765 const float guide_weight, // to balance the amplitudes in the guiding image and the input
766 // image
767 const float min, const float max)
768{
769 assert(ch >= 3);
770 assert(w >= 1);
771
772 gboolean fits = dt_opencl_image_fits_device(devid, width, height, sizeof(float), 18.0f, 0);
773 if(!fits)
774 {
775 // What the device reports free is not what it can be made to have free: most of the
776 // "used" vRAM is the pixelpipe cache holding device copies nobody is reading right now.
777 // Reclaim the idle ones and ask again before writing off the GPU for this call -- the
778 // same flush-then-retry the pipeline itself does around its own per-module fit check.
779 // We are called from inside process_cl(), so the pipe already holds the device lock and
780 // this is the variant that expects the caller to hold it.
782 fits = dt_opencl_image_fits_device(devid, width, height, sizeof(float), 18.0f, 0);
783 }
784 if(!fits)
785 dt_print(DT_DEBUG_OPENCL, "[guided filter] fall back to cpu implementation due to insufficient gpu memory\n");
786
787
788 int err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
789 if(fits)
790 err = guided_filter_cl_impl(devid, guide, in, out, width, height, ch, w, sqrt_eps, guide_weight, min, max);
791 if(err != CL_SUCCESS)
792 {
793 if(guided_filter_cl_fallback(devid, guide, in, out, width, height, ch, w, sqrt_eps, guide_weight, min, max) != 0)
794 return 1;
795 }
796 return 0;
797}
798
799#endif
800// clang-format off
801// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
802// vim: shiftwidth=2 expandtab tabstop=2 cindent
803// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
804// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
int dt_box_mean_horizontal(float *const restrict buf, const size_t width, const int ch, const int radius, float *const restrict user_scratch)
int dt_box_mean(float *const buf, const size_t height, const size_t width, const int ch, const int radius, const unsigned iterations)
int dt_box_mean_vertical(float *const buf, const size_t height, const size_t width, const int ch, const int radius)
#define BOXFILTER_KAHAN_SUM
Definition box_filters.h:37
static const float const float const float min
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
const int res
Definition dtpthread.h:351
void dt_guided_filter_init_cl_global(void)
#define B
static int cl_generate_result(const int devid, const int width, const int height, cl_mem guide, cl_mem a_r, cl_mem a_g, cl_mem a_b, cl_mem b, cl_mem out, const float guide_weight, const float min, const float max)
#define VAR_GG
#define COV_R
#define GUIDE_MEAN_R
#define A_GREEN
static int cl_variances(const int devid, const int width, const int height, cl_mem guide, cl_mem var_imgg_rr, cl_mem var_imgg_rg, cl_mem var_imgg_rb, cl_mem var_imgg_gg, cl_mem var_imgg_gb, cl_mem var_imgg_bb, const float guide_weight)
static int cl_box_mean(const int devid, const int width, const int height, const int w, cl_mem in, cl_mem out, cl_mem temp)
#define GUIDE_MEAN_B
static int guided_filter_cl_fallback(int devid, cl_mem guide, cl_mem in, cl_mem out, const int width, const int height, const int ch, const int w, const float sqrt_eps, const float guide_weight, const float min, const float max)
#define VAR_RR
#define COV_B
void dt_guided_filter_free_cl_global(void)
#define A_BLUE
#define GF_TILE_SIZE
static dt_guided_filter_cl_global_t * _guided_filter_cl_global
static int cl_split_rgb(const int devid, const int width, const int height, cl_mem guide, cl_mem imgg_r, cl_mem imgg_g, cl_mem imgg_b, const float guide_weight)
#define VAR_GB
static float * get_color_pixel(color_image img, size_t i)
#define GUIDE_MEAN_G
static int cl_solve(const int devid, const int width, const int height, cl_mem img_mean, cl_mem imgg_mean_r, cl_mem imgg_mean_g, cl_mem imgg_mean_b, cl_mem cov_imgg_img_r, cl_mem cov_imgg_img_g, cl_mem cov_imgg_img_b, cl_mem var_imgg_rr, cl_mem var_imgg_rg, cl_mem var_imgg_rb, cl_mem var_imgg_gg, cl_mem var_imgg_gb, cl_mem var_imgg_bb, cl_mem a_r, cl_mem a_g, cl_mem a_b, cl_mem b)
static int cl_update_covariance(const int devid, const int width, const int height, cl_mem in, cl_mem out, cl_mem a, cl_mem b, float eps)
#define COV_G
static int cl_covariances(const int devid, const int width, const int height, cl_mem guide, cl_mem in, cl_mem cov_imgg_img_r, cl_mem cov_imgg_img_g, cl_mem cov_imgg_img_b, const float guide_weight)
static __DT_CLONE_TARGETS__ int guided_filter_tiling(color_image imgg, gray_image img, gray_image img_out, tile target, const int w, const float eps, const float guide_weight, const float min, const float max)
#define VAR_RB
#define A_RED
int guided_filter_cl(int devid, cl_mem guide, cl_mem in, cl_mem out, const int width, const int height, const int ch, const int w, const float sqrt_eps, const float guide_weight, const float min, const float max)
#define INP_MEAN
static int guided_filter_cl_impl(int devid, cl_mem guide, cl_mem in, cl_mem out, const int width, const int height, const int ch, const int w, const float sqrt_eps, const float guide_weight, const float min, const float max)
#define VAR_RG
__DT_CLONE_TARGETS__ int guided_filter(const float *const guide, const float *const in, float *const out, const int width, const int height, const int ch, const int w, const float sqrt_eps, const float guide_weight, const float min, const float max)
#define VAR_BB
static int max_i(int a, int b)
static int min_i(int a, int b)
static float kernel(const float *x, const float *y)
@ 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.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#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_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2894
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
gboolean dt_opencl_image_fits_device(const int devid, const size_t width, const size_t height, const unsigned bpp, const float factor, const size_t overhead)
Definition opencl.c:3235
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
int dt_opencl_read_host_from_device(const int devid, void *host, void *device, const int width, const int height, const int bpp)
Definition opencl.c:2587
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
int dt_opencl_write_host_to_device(const int devid, void *host, void *device, const int width, const int height, const int bpp)
Definition opencl.c:2634
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
void dt_dev_pixelpipe_cache_flush_clmem(const int devid)
Release cached OpenCL buffers for a single device.
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
#define dt_get_perthread(buf, padsize)
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
#define eps
Definition rcd.c:81
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
float * data
int lower
int left
int right
int upper
#define __DT_CLONE_TARGETS__