Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
locallaplaciancl.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016-2017 johannes hanika.
4 Copyright (C) 2017 Ulrich Pegelow.
5 Copyright (C) 2019-2020 Aurélien PIERRE.
6 Copyright (C) 2020 Heiko Bauke.
7 Copyright (C) 2020 Pascal Obry.
8 Copyright (C) 2021 Chris Elston.
9 Copyright (C) 2022 Hanno Schwalm.
10 Copyright (C) 2022 Martin Bařinka.
11
12 darktable is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 darktable is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with darktable. If not, see <http://www.gnu.org/licenses/>.
24*/
25#ifdef HAVE_OPENCL
26#include "common/darktable.h"
27#include "common/opencl.h"
29
30#define max_levels 30
31#define num_gamma 6
32
33// downsample width/height to given level
34static inline uint64_t dl(uint64_t size, const int level)
35{
36 for(int l=0;l<level;l++)
37 size = (size-1)/2+1;
38 return size;
39}
40
42{
44
45 const int program = 19; // locallaplacian.cl, from programs.conf
46 g->kernel_pad_input = dt_opencl_create_kernel(program, "pad_input");
47 g->kernel_gauss_expand = dt_opencl_create_kernel(program, "gauss_expand");
48 g->kernel_gauss_reduce = dt_opencl_create_kernel(program, "gauss_reduce");
49 g->kernel_laplacian_assemble = dt_opencl_create_kernel(program, "laplacian_assemble");
50 g->kernel_process_curve = dt_opencl_create_kernel(program, "process_curve");
51 g->kernel_write_back = dt_opencl_create_kernel(program, "write_back");
52 return g;
53}
54
56{
57 if(IS_NULL_PTR(g)) return;
58
59 dt_opencl_free_kernel(g->kernel_pad_input);
60 dt_opencl_free_kernel(g->kernel_gauss_expand);
61 dt_opencl_free_kernel(g->kernel_gauss_reduce);
62 dt_opencl_free_kernel(g->kernel_laplacian_assemble);
63 dt_opencl_free_kernel(g->kernel_process_curve);
64 dt_opencl_free_kernel(g->kernel_write_back);
65
66 dt_free(g);
67}
68
70{
71 if(IS_NULL_PTR(g)) return;
72
73 // free device mem
74 for(int l=0;l<max_levels;l++)
75 {
76 dt_opencl_release_mem_object(g->dev_padded[l]);
77 dt_opencl_release_mem_object(g->dev_output[l]);
78 for(int k=0;k<num_gamma;k++)
79 dt_opencl_release_mem_object(g->dev_processed[k][l]);
80 }
81 for(int k=0;k<num_gamma;k++) dt_free(g->dev_processed[k]);
82 dt_free(g->dev_padded);
83 dt_free(g->dev_output);
84 dt_free(g->dev_processed);
85 g->dev_padded = g->dev_output = 0;
86 g->dev_processed = 0;
87 dt_free(g);
88}
89
91 const int devid,
92 const int width, // width of input image
93 const int height, // height of input image
94 const float sigma, // user param: separate shadows/mid-tones/highlights
95 const float shadows, // user param: lift shadows
96 const float highlights, // user param: compress highlights
97 const float clarity) // user param: increase clarity/local contrast
98{
100 if(IS_NULL_PTR(g)) return NULL;
101
103 g->devid = devid;
104 g->width = width;
105 g->height = height;
106 g->sigma = sigma;
107 g->shadows = shadows;
108 g->highlights = highlights;
109 g->clarity = clarity;
110 g->dev_padded = calloc(max_levels, sizeof(cl_mem));
111 g->dev_output = calloc(max_levels, sizeof(cl_mem));
112 g->dev_processed = calloc(num_gamma, sizeof(cl_mem *));
113 for(int k=0;k<num_gamma;k++)
114 g->dev_processed[k] = calloc(max_levels, sizeof(cl_mem));
115
116 g->num_levels = MIN(max_levels, 31-__builtin_clz(MIN(width,height)));
117 g->max_supp = 1<<(g->num_levels-1);
118 g->bwidth = ROUNDUPDWD(width + 2*g->max_supp, devid);
119 g->bheight = ROUNDUPDHT(height + 2*g->max_supp, devid);
120
121 // get intermediate vector buffers with read-write access
122 for(int l=0;l<g->num_levels;l++)
123 {
124 g->dev_padded[l] = dt_opencl_alloc_device(devid, ROUNDUPDWD(dl(g->bwidth, l), devid), ROUNDUPDHT(dl(g->bheight, l), devid), sizeof(float));
125 if(!g->dev_padded[l]) goto error;
126 g->dev_output[l] = dt_opencl_alloc_device(devid, ROUNDUPDWD(dl(g->bwidth, l), devid), ROUNDUPDHT(dl(g->bheight, l), devid), sizeof(float));
127 if(!g->dev_output[l]) goto error;
128 for(int k=0;k<num_gamma;k++)
129 {
130 g->dev_processed[k][l] = dt_opencl_alloc_device(devid, ROUNDUPDWD(dl(g->bwidth, l), devid), ROUNDUPDHT(dl(g->bheight, l), devid), sizeof(float));
131 if(!g->dev_processed[k][l]) goto error;
132 }
133 }
134
135 return g;
136
137error:
138 fprintf(stderr, "[local laplacian cl] could not allocate temporary buffers\n");
140 return NULL;
141}
142
144 dt_local_laplacian_cl_t *b, // opencl context with temp buffers
145 cl_mem input, // input buffer in some Labx or yuvx format
146 cl_mem output) // output buffer with colour
147{
148 cl_int err = -666;
149
150 if(b->bwidth <= 1 || b->bheight <= 1) return err;
151
152 size_t sizes_pad[] = { ROUNDUPDWD(b->bwidth, b->devid), ROUNDUPDHT(b->bheight, b->devid), 1 };
153 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 0, sizeof(cl_mem), &input);
154 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 1, sizeof(cl_mem), &b->dev_padded[0]);
155 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 2, sizeof(int), &b->width);
156 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 3, sizeof(int), &b->height);
157 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 4, sizeof(int), &b->max_supp);
158 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 5, sizeof(int), &b->bwidth);
159 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_pad_input, 6, sizeof(int), &b->bheight);
160 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_pad_input, sizes_pad);
161 if(err != CL_SUCCESS) goto error;
162
163 // create gauss pyramid of padded input, write coarse directly to output
164 for(int l=1;l<b->num_levels;l++)
165 {
166 const int wd = dl(b->bwidth, l), ht = dl(b->bheight, l);
167 size_t sizes[] = { ROUNDUPDWD(wd, b->devid), ROUNDUPDHT(ht, b->devid), 1 };
168 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 0, sizeof(cl_mem), &b->dev_padded[l-1]);
169 if(l == b->num_levels-1)
170 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 1, sizeof(cl_mem), &b->dev_output[l]);
171 else
172 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 1, sizeof(cl_mem), &b->dev_padded[l]);
173 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 2, sizeof(int), &wd);
174 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 3, sizeof(int), &ht);
175 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_gauss_reduce, sizes);
176 if(err != CL_SUCCESS) goto error;
177 }
178
179 for(int k=0;k<num_gamma;k++)
180 { // process images
181 const float g = (k+.5f)/(float)num_gamma;
182 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 0, sizeof(cl_mem), &b->dev_padded[0]);
183 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 1, sizeof(cl_mem), &b->dev_processed[k][0]);
184 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 2, sizeof(float), &g);
185 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 3, sizeof(float), &b->sigma);
186 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 4, sizeof(float), &b->shadows);
187 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 5, sizeof(float), &b->highlights);
188 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 6, sizeof(float), &b->clarity);
189 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 7, sizeof(int), &b->bwidth);
190 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_process_curve, 8, sizeof(int), &b->bheight);
191 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_process_curve, sizes_pad);
192 if(err != CL_SUCCESS) goto error;
193
194 // create gaussian pyramids
195 for(int l=1;l<b->num_levels;l++)
196 {
197 const int wd = dl(b->bwidth, l), ht = dl(b->bheight, l);
198 size_t sizes[] = { ROUNDUPDWD(wd, b->devid), ROUNDUPDHT(ht, b->devid), 1 };
199 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 0, sizeof(cl_mem), &b->dev_processed[k][l-1]);
200 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 1, sizeof(cl_mem), &b->dev_processed[k][l]);
201 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 2, sizeof(int), &wd);
202 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_gauss_reduce, 3, sizeof(int), &ht);
203 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_gauss_reduce, sizes);
204 if(err != CL_SUCCESS) goto error;
205 }
206 }
207
208 // assemble output pyramid coarse to fine
209 for(int l=b->num_levels-2;l >= 0; l--)
210 {
211 const int pw = dl(b->bwidth,l), ph = dl(b->bheight,l);
212 size_t sizes[] = { ROUNDUPDWD(pw, b->devid), ROUNDUPDHT(ph, b->devid), 1 };
213 // this is so dumb:
214 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 0, sizeof(cl_mem), &b->dev_padded[l]);
215 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 1, sizeof(cl_mem), &b->dev_output[l+1]);
216 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 2, sizeof(cl_mem), &b->dev_output[l]);
217 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 3, sizeof(cl_mem), &b->dev_processed[0][l]);
218 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 4, sizeof(cl_mem), &b->dev_processed[0][l+1]);
219 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 5, sizeof(cl_mem), &b->dev_processed[1][l]);
220 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 6, sizeof(cl_mem), &b->dev_processed[1][l+1]);
221 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 7, sizeof(cl_mem), &b->dev_processed[2][l]);
222 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 8, sizeof(cl_mem), &b->dev_processed[2][l+1]);
223 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 9, sizeof(cl_mem), &b->dev_processed[3][l]);
224 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 10, sizeof(cl_mem), &b->dev_processed[3][l+1]);
225 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 11, sizeof(cl_mem), &b->dev_processed[4][l]);
226 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 12, sizeof(cl_mem), &b->dev_processed[4][l+1]);
227 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 13, sizeof(cl_mem), &b->dev_processed[5][l]);
228 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 14, sizeof(cl_mem), &b->dev_processed[5][l+1]);
229 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 15, sizeof(cl_mem), &b->dev_processed[6][l]);
230 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 16, sizeof(cl_mem), &b->dev_processed[6][l+1]);
231 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 17, sizeof(cl_mem), &b->dev_processed[7][l]);
232 // dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 18, sizeof(cl_mem), &b->dev_processed[7][l+1]);
233 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 15, sizeof(int), &pw);
234 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_laplacian_assemble, 16, sizeof(int), &ph);
235 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_laplacian_assemble, sizes);
236 if(err != CL_SUCCESS) goto error;
237 }
238
239 // read back processed L channel and copy colours:
240 size_t sizes[] = { ROUNDUPDWD(b->width, b->devid), ROUNDUPDHT(b->height, b->devid), 1 };
241 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 0, sizeof(cl_mem), &input);
242 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 1, sizeof(cl_mem), &b->dev_output[0]);
243 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 2, sizeof(cl_mem), &output);
244 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 3, sizeof(int), &b->max_supp);
245 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 4, sizeof(int), &b->width);
246 dt_opencl_set_kernel_arg(b->devid, b->global->kernel_write_back, 5, sizeof(int), &b->height);
247 err = dt_opencl_enqueue_kernel_2d(b->devid, b->global->kernel_write_back, sizes);
248 if(err != CL_SUCCESS) goto error;
249
250 return CL_SUCCESS;
251
252error:
253 fprintf(stderr, "[local laplacian cl] failed: %d\n", err);
254 return err;
255}
256
257#undef max_levels
258#undef num_gamma
259#endif
260// clang-format off
261// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
262// vim: shiftwidth=2 expandtab tabstop=2 cindent
263// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
264// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
darktable_t darktable
Definition darktable.c:181
#define dt_free(ptr)
Definition darktable.h:456
#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:281
cl_int dt_local_laplacian_cl(dt_local_laplacian_cl_t *b, cl_mem input, cl_mem output)
dt_local_laplacian_cl_global_t * dt_local_laplacian_init_cl_global()
void dt_local_laplacian_free_cl_global(dt_local_laplacian_cl_global_t *g)
#define num_gamma
#define max_levels
dt_local_laplacian_cl_t * dt_local_laplacian_init_cl(const int devid, const int width, const int height, const float sigma, const float shadows, const float highlights, const float clarity)
static uint64_t dl(uint64_t size, const int level)
void dt_local_laplacian_free_cl(dt_local_laplacian_cl_t *g)
float *const restrict const size_t k
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:2136
void * dt_opencl_alloc_device(const int devid, const int width, const int height, const int bpp)
Definition opencl.c:2471
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
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:2127
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
const float sigma
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_opencl_t * opencl
Definition darktable.h:785
struct dt_local_laplacian_cl_global_t * local_laplacian
Definition opencl.h:263
#define MIN(a, b)
Definition thinplate.c:32