Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
highpass.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2011-2012 Henrik Andersson.
4 Copyright (C) 2011-2013, 2016 johannes hanika.
5 Copyright (C) 2011 Robert Bieber.
6 Copyright (C) 2011-2012, 2014, 2016-2017 Ulrich Pegelow.
7 Copyright (C) 2012 Richard Wonka.
8 Copyright (C) 2012-2014, 2016, 2019 Tobias Ellinghaus.
9 Copyright (C) 2013-2016 Roman Lebedev.
10 Copyright (C) 2013 Simon Spannagel.
11 Copyright (C) 2015 Pedro Côrte-Real.
12 Copyright (C) 2017 Heiko Bauke.
13 Copyright (C) 2018, 2020, 2022-2023, 2025-2026 Aurélien PIERRE.
14 Copyright (C) 2018 Edgardo Hoszowski.
15 Copyright (C) 2018 Maurizio Paglia.
16 Copyright (C) 2018-2020, 2022 Pascal Obry.
17 Copyright (C) 2018 rawfiner.
18 Copyright (C) 2019 Andreas Schneider.
19 Copyright (C) 2019 Diederik ter Rahe.
20 Copyright (C) 2020 Aldric Renaudin.
21 Copyright (C) 2020, 2022 Diederik Ter Rahe.
22 Copyright (C) 2020 Hubert Kowalski.
23 Copyright (C) 2020 Ralf Brown.
24 Copyright (C) 2022 Hanno Schwalm.
25 Copyright (C) 2022 Martin Bařinka.
26 Copyright (C) 2022 Philipp Lutz.
27
28 darktable is free software: you can redistribute it and/or modify
29 it under the terms of the GNU General Public License as published by
30 the Free Software Foundation, either version 3 of the License, or
31 (at your option) any later version.
32
33 darktable is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
37
38 You should have received a copy of the GNU General Public License
39 along with darktable. If not, see <http://www.gnu.org/licenses/>.
40*/
41#ifdef HAVE_CONFIG_H
42#include "common/darktable.h"
43#include "config.h"
44#endif
45#include <assert.h>
46#include <stdlib.h>
47#include <string.h>
48
49#include "bauhaus/bauhaus.h"
50#include "common/box_filters.h"
51#include "common/math.h"
52#include "common/opencl.h"
53#include "control/control.h"
54#include "develop/develop.h"
55#include "develop/imageop.h"
56#include "develop/imageop_gui.h"
57#include "develop/tiling.h"
58
59#include "gui/gtk.h"
60#include "iop/iop_api.h"
61#include <gtk/gtk.h>
62#include <inttypes.h>
63
64#define MAX_RADIUS 16
65
67
69{
70 float sharpness; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 50.0
71 float contrast; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 50.0 $DESCRIPTION: "contrast boost"
73
78
84
92
93
94const char *name()
95{
96 return _("highpass");
97}
98
99const char **description(struct dt_iop_module_t *self)
100{
101 return dt_iop_set_description(self, _("isolate high frequencies in the image"),
102 _("creative"),
103 _("linear or non-linear, Lab, scene-referred"),
104 _("frequential, Lab"),
105 _("special, Lab, scene-referred"));
106}
107
112
114{
115 return IOP_GROUP_EFFECTS;
116}
117
119{
120 return IOP_CS_LAB;
121}
122
123void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
124{
125 (void)self;
126 (void)pipe;
127 const dt_iop_roi_t *const roi_in = &piece->roi_in;
129
130 const int rad = MAX_RADIUS * (fmin(100.0f, d->sharpness + 1) / 100.0f);
131 const int radius = MIN(MAX_RADIUS, ceilf(rad * roi_in->scale));
132
133 const float sigma = sqrtf((radius * (radius + 1) * BOX_ITERATIONS + 2) / 3.0f);
134 const int wdh = ceilf(3.0f * sigma);
135
136 tiling->factor = 2.1f; // in + out + small slice for box_mean
137 tiling->factor_cl = 3.0f; // in + out + tmp
138 tiling->maxbuf = 1.0f;
139 tiling->overhead = 0;
140 tiling->overlap = wdh;
141 tiling->xalign = 1;
142 tiling->yalign = 1;
143 return;
144}
145
146
147#ifdef HAVE_OPENCL
148int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
149{
150 const dt_iop_roi_t *const roi_in = &piece->roi_in;
153
154 cl_int err = -999;
155 cl_mem dev_tmp = NULL;
156 cl_mem dev_m = NULL;
157
158 const int devid = pipe->devid;
159 const int width = roi_in->width;
160 const int height = roi_in->height;
161
162 const int rad = MAX_RADIUS * (fmin(100.0f, d->sharpness + 1) / 100.0f);
163 const int radius = MIN(MAX_RADIUS, ceilf(rad * roi_in->scale));
164
165 /* sigma-radius correlation to match opencl vs. non-opencl. identified by numerical experiments but
166 * unproven. ask me if you need details. ulrich */
167 const float sigma = sqrtf((radius * (radius + 1) * BOX_ITERATIONS + 2) / 3.0f);
168 const int wdh = ceilf(3.0f * sigma);
169 const int wd = 2 * wdh + 1;
170 const size_t mat_size = (size_t)wd * sizeof(float);
171 float *mat = malloc(mat_size);
172 float *m = mat + wdh;
173 float weight = 0.0f;
174
175 // init gaussian kernel
176 for(int l = -wdh; l <= wdh; l++) weight += m[l] = expf(-(l * l) / (2.f * sigma * sigma));
177 for(int l = -wdh; l <= wdh; l++) m[l] /= weight;
178
179 // for(int l=-wdh; l<=wdh; l++) printf("%.6f ", (double)m[l]);
180 // printf("\n");
181
182 float contrast_scale = ((d->contrast / 100.0f) * 7.5f);
183
184 int hblocksize;
186 = (dt_opencl_local_buffer_t){ .xoffset = 2 * wdh, .xfactor = 1, .yoffset = 0, .yfactor = 1,
187 .cellsize = sizeof(float), .overhead = 0,
188 .sizex = 1 << 16, .sizey = 1 };
189
190 if(dt_opencl_local_buffer_opt(devid, gd->kernel_highpass_hblur, &hlocopt))
191 hblocksize = hlocopt.sizex;
192 else
193 hblocksize = 1;
194
195 int vblocksize;
197 = (dt_opencl_local_buffer_t){ .xoffset = 1, .xfactor = 1, .yoffset = 2 * wdh, .yfactor = 1,
198 .cellsize = sizeof(float), .overhead = 0,
199 .sizex = 1, .sizey = 1 << 16 };
200
201 if(dt_opencl_local_buffer_opt(devid, gd->kernel_highpass_vblur, &vlocopt))
202 vblocksize = vlocopt.sizey;
203 else
204 vblocksize = 1;
205
206
207 const size_t bwidth = ROUNDUP(width, hblocksize);
208 const size_t bheight = ROUNDUP(height, vblocksize);
209
210 size_t sizes[3];
211 size_t local[3];
212
213 dev_tmp = dt_opencl_alloc_device(devid, width, height, sizeof(float) * 4);
214 if(IS_NULL_PTR(dev_tmp)) goto error;
215
216 dev_m = dt_opencl_copy_host_to_device_constant(devid, mat_size, mat);
217 if(IS_NULL_PTR(dev_m)) goto error;
218
219 /* invert image */
220 sizes[0] = ROUNDUPDWD(width, devid);
221 sizes[1] = ROUNDUPDHT(height, devid);
222 sizes[2] = 1;
223 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_invert, 0, sizeof(cl_mem), (void *)&dev_in);
224 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_invert, 1, sizeof(cl_mem), (void *)&dev_tmp);
225 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_invert, 2, sizeof(int), (void *)&width);
226 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_invert, 3, sizeof(int), (void *)&height);
228 if(err != CL_SUCCESS) goto error;
229
230 if(rad != 0)
231 {
232 /* horizontal blur */
233 sizes[0] = bwidth;
234 sizes[1] = ROUNDUPDHT(height, devid);
235 sizes[2] = 1;
236 local[0] = hblocksize;
237 local[1] = 1;
238 local[2] = 1;
239 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 0, sizeof(cl_mem), (void *)&dev_tmp);
240 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 1, sizeof(cl_mem), (void *)&dev_out);
241 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 2, sizeof(cl_mem), (void *)&dev_m);
242 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 3, sizeof(int), (void *)&wdh);
243 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 4, sizeof(int), (void *)&width);
244 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 5, sizeof(int), (void *)&height);
245 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 6, sizeof(int), (void *)&hblocksize);
246 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_hblur, 7, (hblocksize + 2 * wdh) * sizeof(float), NULL);
248 if(err != CL_SUCCESS) goto error;
249
250
251 /* vertical blur */
252 sizes[0] = ROUNDUPDWD(width, devid);
253 sizes[1] = bheight;
254 sizes[2] = 1;
255 local[0] = 1;
256 local[1] = vblocksize;
257 local[2] = 1;
258 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 0, sizeof(cl_mem), (void *)&dev_out);
259 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 1, sizeof(cl_mem), (void *)&dev_tmp);
260 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 2, sizeof(cl_mem), (void *)&dev_m);
261 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 3, sizeof(int), (void *)&wdh);
262 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 4, sizeof(int), (void *)&width);
263 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 5, sizeof(int), (void *)&height);
264 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 6, sizeof(int), (void *)&vblocksize);
265 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_vblur, 7, (vblocksize + 2 * wdh) * sizeof(float), NULL);
267 if(err != CL_SUCCESS) goto error;
268 }
269
270 /* mixing tmp and in -> out */
271 sizes[0] = ROUNDUPDWD(width, devid);
272 sizes[1] = ROUNDUPDHT(height, devid);
273 sizes[2] = 1;
274 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_mix, 0, sizeof(cl_mem), (void *)&dev_in);
275 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_mix, 1, sizeof(cl_mem), (void *)&dev_tmp);
276 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_mix, 2, sizeof(cl_mem), (void *)&dev_out);
277 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_mix, 3, sizeof(int), (void *)&width);
278 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_mix, 4, sizeof(int), (void *)&height);
279 dt_opencl_set_kernel_arg(devid, gd->kernel_highpass_mix, 5, sizeof(float), (void *)&contrast_scale);
280 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_highpass_mix, sizes);
281 if(err != CL_SUCCESS) goto error;
282
285 dt_free(mat);
286 return TRUE;
287
288error:
291 dt_free(mat);
292 dt_print(DT_DEBUG_OPENCL, "[opencl_highpass] couldn't enqueue kernel! %d\n", err);
293 return FALSE;
294}
295#endif
296
298int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
299 void *const ovoid)
300{
301 const dt_iop_roi_t *const roi_in = &piece->roi_in;
302 const dt_iop_roi_t *const roi_out = &piece->roi_out;
304 const float *const in = (float *)ivoid;
305 float *out = (float *)ovoid;
306 const int ch = 4;
307
308/* create inverted image and then blur */
309/* since we use only the L channel, pack the values together instead of every fourth float */
310/* to reduce cache pressure and memory bandwidth during the blur operation */
311 const size_t npixels = (size_t)roi_out->height * roi_out->width;
313 for(size_t k = 0; k < (size_t)npixels; k++)
314 out[k] = 100.0f - LCLIP(in[4 * k]); // only L in Lab space
315
316 const int rad = MAX_RADIUS * (fmin(100.0, data->sharpness + 1) / 100.0);
317 const int radius = MIN(MAX_RADIUS, ceilf(rad * roi_in->scale));
318
319 /* horizontal blur out into out */
320 const int range = 2 * radius + 1;
321 const int hr = range / 2;
322
323 if(dt_box_mean(out, roi_out->height, roi_out->width, 1, hr, BOX_ITERATIONS) != 0)
324 {
325 return 1;
326 }
327
328 const float contrast_scale = ((data->contrast / 100.0) * 7.5);
329 /* Blend the inverted blurred L channel with the original input. Because we packed the L values */
330 /* and are inserting the result in the same buffer containing the L values, we need to work in */
331 /* reverse order */
332 /* We can only do the final 3/4 in parallel here, because updating the first quarter in one thread */
333 /* would clobber values still needed by other threads. */
335 for(size_t k = npixels - 1; k > npixels/4; k--)
336 {
337 size_t index = ch * k;
338 // Mix out and in
339 const float L = out[k] * 0.5 + in[index] * 0.5;
340 out[index] = LCLIP(50.0f + ((L - 50.0f) * contrast_scale));
341 out[index + 1] = out[index + 2] = 0.0f; // desaturate a and b in Lab space
342 out[index + 3] = in[index + 3]; // copy the alpha channel in case it is in use
343 }
344 /* process the final quarter of the pixels */
345 for(ssize_t k = npixels/4; k >= 0; k--)
346 {
347 size_t index = ch * k;
348 // Mix out and in
349 const float L = out[k] * 0.5 + in[index] * 0.5;
350 out[index] = LCLIP(50.0f + ((L - 50.0f) * contrast_scale));
351 out[index + 1] = out[index + 2] = 0.0f; // desaturate a and b in Lab space
352 out[index + 3] = in[index + 3]; // copy the alpha channel in case it is in use
353 }
354
355 return 0;
356}
357
360{
363
364 d->sharpness = p->sharpness;
365 d->contrast = p->contrast;
366}
367
369{
371 piece->data_size = sizeof(dt_iop_highpass_data_t);
372}
373
375{
376 dt_free_align(piece->data);
377 piece->data = NULL;
378}
379
381{
382 const int program = 4; // highpass.cl, from programs.conf
385 module->data = gd;
386 gd->kernel_highpass_invert = dt_opencl_create_kernel(program, "highpass_invert");
387 gd->kernel_highpass_hblur = dt_opencl_create_kernel(program, "highpass_hblur");
388 gd->kernel_highpass_vblur = dt_opencl_create_kernel(program, "highpass_vblur");
389 gd->kernel_highpass_mix = dt_opencl_create_kernel(program, "highpass_mix");
390}
391
401
402
403void gui_init(struct dt_iop_module_t *self)
404{
406
407 g->sharpness = dt_bauhaus_slider_from_params(self, N_("sharpness"));
408 dt_bauhaus_slider_set_format(g->sharpness, "%");
409 gtk_widget_set_tooltip_text(g->sharpness, _("the sharpness of highpass filter"));
410
411 g->contrast = dt_bauhaus_slider_from_params(self, "contrast");
412 dt_bauhaus_slider_set_format(g->contrast, "%");
413 gtk_widget_set_tooltip_text(g->contrast, _("the contrast of highpass filter"));
414}
415// clang-format off
416// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
417// vim: shiftwidth=2 expandtab tabstop=2 cindent
418// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
419// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:278
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
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 BOX_ITERATIONS
Definition box_filters.h:32
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_LAB
const dt_colormatrix_t dt_aligned_pixel_t out
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:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#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
void dt_iop_params_t
Definition dev_history.h:41
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:30
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition highpass.c:358
const char ** description(struct dt_iop_module_t *self)
Definition highpass.c:99
int default_group()
Definition highpass.c:113
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition highpass.c:298
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition highpass.c:368
const char * name()
Definition highpass.c:94
#define MAX_RADIUS
Definition highpass.c:64
void gui_init(struct dt_iop_module_t *self)
Definition highpass.c:403
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
Definition highpass.c:123
void cleanup_global(dt_iop_module_so_t *module)
Definition highpass.c:392
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition highpass.c:118
int flags()
Definition highpass.c:108
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition highpass.c:374
void init_global(dt_iop_module_so_t *module)
Definition highpass.c:380
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition highpass.c:148
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:169
@ IOP_GROUP_EFFECTS
Definition imageop.h:142
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
void *const ovoid
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define LCLIP(x)
Definition math.h:85
int dt_opencl_local_buffer_opt(const int devid, const int kernel, dt_opencl_local_buffer_t *factors)
Definition opencl.c:3156
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_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2332
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
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:2142
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
#define ROUNDUP(a, n)
Definition opencl.h:78
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
struct _GtkWidget GtkWidget
Definition splash.h:29
const float sigma
struct dt_iop_module_t *void * data
dt_iop_global_data_t * data
Definition imageop.h:233
dt_iop_global_data_t * global_data
Definition imageop.h:314
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
#define MIN(a, b)
Definition thinplate.c:32