Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
censorize.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020-2021, 2023, 2025-2026 Aurélien PIERRE.
4 Copyright (C) 2021 Hubert Kowalski.
5 Copyright (C) 2021-2022 Pascal Obry.
6 Copyright (C) 2021 Ralf Brown.
7 Copyright (C) 2022 Diederik Ter Rahe.
8 Copyright (C) 2022 Hanno Schwalm.
9 Copyright (C) 2022 Martin Bařinka.
10 Copyright (C) 2022 Philipp Lutz.
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
26#ifdef HAVE_CONFIG_H
27#include "common/darktable.h"
28#include "config.h"
29#endif
30#include "bauhaus/bauhaus.h"
31#include "common/debug.h"
32#include "common/gaussian.h"
33#include "common/opencl.h"
34#include "common/imagebuf.h"
35#include "control/control.h"
36#include "develop/develop.h"
37#include "develop/imageop.h"
39#include "develop/imageop_gui.h"
41#include "develop/tiling.h"
42
43#include "gui/gtk.h"
44#include "gui/presets.h"
45#include "iop/iop_api.h"
46#include <assert.h>
47#include <gtk/gtk.h>
48#include <math.h>
49#include <stdlib.h>
50#include <string.h>
51
52#include <inttypes.h>
53
55
57{
58 float radius_1; // $MIN: 0.0 $MAX: 500.0 $DEFAULT: 0.0 $DESCRIPTION: "input blur radius"
59 float pixelate; // $MIN: 0.0 $MAX: 500.0 $DEFAULT: 0.0 $DESCRIPTION: "pixellation radius"
60 float radius_2; // $MIN: 0.0 $MAX: 500.0 $DEFAULT: 0.0 $DESCRIPTION: "output blur radius"
61 float noise; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "noise level"
63
64
69
71
76
77typedef struct point_t
78{
79 size_t x, y;
81
82const char *
84{
85 return _("censorize");
86}
87
88const char **description(struct dt_iop_module_t *self)
89{
90 return dt_iop_set_description(self, _("censorize license plates and body parts for privacy"),
91 _("creative"),
92 _("linear or non-linear, RGB, scene-referred"),
93 _("frequential, RGB"),
94 _("special, RGB, scene-referred"));
95}
96
101
103{
104 return IOP_GROUP_EFFECTS;
105}
106
108{
109 return IOP_CS_RGB;
110}
111
113static inline void make_noise(float *const output, const float noise, const size_t width, const size_t height)
114{
115 __OMP_PARALLEL_FOR_SIMD__(aligned(output:64) collapse(2))
116 for(size_t i = 0; i < height; i++)
117 for(size_t j = 0; j < width; j++)
118 {
119 // Init random number generator
120 uint32_t DT_ALIGNED_ARRAY state[4] = { splitmix32(j + 1), splitmix32((j + 1) * (i + 3)), splitmix32(1337), splitmix32(666) };
125
126 const size_t index = (i * width + j) * 4;
127 float *const restrict pix_out = __builtin_assume_aligned(output + index, 16);
128 const float norm = pix_out[1];
129
130 // create statistical noise
131 const float epsilon = gaussian_noise(norm, noise * norm, i % 2 || j % 2, state) / norm;
132
133 // add noise to output
134 for(size_t c = 0; c < 3; c++) pix_out[c] = fmaxf(pix_out[c] * epsilon, 0.f);
135 }
136}
137
138
140int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
141 void *const ovoid)
142{
143 const dt_iop_roi_t *const roi_in = &piece->roi_in;
144 const dt_iop_roi_t *const roi_out = &piece->roi_out;
145
147 const float *const restrict in = DT_IS_ALIGNED((const float *const restrict)ivoid);
148 float *const restrict out = DT_IS_ALIGNED((float *const restrict)ovoid);
149
150 const int width = roi_in->width;
151 const int height = roi_in->height;
152 const int ch = 4;
153
154 float *const restrict temp = dt_pixelpipe_cache_alloc_align_float((size_t)width * height * ch, pipe);
155 if(IS_NULL_PTR(temp)) return 1;
156
157 const float module_scale = dt_dev_get_module_scale(pipe, roi_in);
158 const float sigma_1 = data->radius_1 / module_scale;
159 const float sigma_2 = data->radius_2 / module_scale;
160 const size_t pixel_radius = data->pixelate / module_scale;
161
162 // used to adjuste blur level depending on size. Don't amplify noise if magnified > 100%
163 const float scale = fmaxf(module_scale, 1.f);
164 const float noise = data->noise / scale;
165
166 dt_aligned_pixel_t RGBmax, RGBmin;
167 for(int k = 0; k < 4; k++)
168 {
169 RGBmax[k] = INFINITY;
170 RGBmin[k] = 0.f;
171 }
172
173 const float *restrict input = in;
174 float *restrict output = out;
175
176 // first blurring step
177 if(sigma_1 != 0.f)
178 {
179 dt_gaussian_t *g = dt_gaussian_init(width, height, ch, RGBmax, RGBmin, sigma_1, 0);
180 if(IS_NULL_PTR(g))
181 {
183 return 1;
184 }
185 dt_gaussian_blur_4c(g, input, output);
187
188 input = output;
189 }
190
191 output = temp;
192
193 // pixelate
194 if(pixel_radius != 0)
195 {
196 const size_t pixels_x = width / (2 * pixel_radius);
197 const size_t pixels_y = height / (2 * pixel_radius);
198 __OMP_PARALLEL_FOR__(collapse(2))
199 for(size_t j = 0; j < pixels_y + 1; j++)
200 for(size_t i = 0; i < pixels_x + 1; i++)
201 {
202 // get the top left coordinate of the big pixel
203 const point_t tl = { CLAMP(2 * pixel_radius * i, 0, width - 1),
204 CLAMP(2 * pixel_radius * j, 0, height - 1) };
205 // get the center of the big pixel
206 const point_t cc = { CLAMP(tl.x + pixel_radius, 0, width - 1),
207 CLAMP(tl.y + pixel_radius, 0, height - 1) };
208 // get the bottom right coordinate of the big pixel
209 const point_t br = { CLAMP(cc.x + pixel_radius, 0, width - 1),
210 CLAMP(cc.y + pixel_radius, 0, height - 1) };
211
212 // get the bounding box + center point coordinates
213 const point_t box[5] = { tl, { br.x, tl.y }, cc, { tl.x, br.y }, br };
214
215 // find the average color over the big pixel
216 dt_aligned_pixel_t RGB = { 0.f };
217 for(size_t k = 0; k < 5; k++)
218 {
219 const float *const restrict pix_in = __builtin_assume_aligned(input + (width * box[k].y + box[k].x) * 4, 16);
221 RGB[c] += pix_in[c] / 5.f;
222 }
223
224 // paint the big pixel with solid color == average
225 for(size_t jj = tl.y; jj < br.y; jj++)
226 for(size_t ii = tl.x; ii < br.x; ii++)
227 {
228 float *const restrict pix_out = __builtin_assume_aligned(output + (jj * width + ii) * 4, 16);
230 pix_out[c] = RGB[c];
231 }
232 }
233
234 input = output;
235 }
236
237 // second blurring step
238 if(sigma_2 != 0.f)
239 {
240 output = out;
241
242 if(noise != 0.f)
243 make_noise(output, noise, width, height);
244
245 dt_gaussian_t *g = dt_gaussian_init(width, height, ch, RGBmax, RGBmin, sigma_2, 0);
246 if(IS_NULL_PTR(g))
247 {
249 return 1;
250 }
251 dt_gaussian_blur_4c(g, input, output);
253 }
254 else
255 {
256 output = out;
257 dt_simd_memcpy(input, output, (size_t)width * height * ch);
258 }
259
260 if(noise != 0.f)
261 make_noise(output, noise, width, height);
262
264 dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
265
267 return 0;
268}
269
270
271// OpenCL not implemented yet, but the following only needs a slight modification to get it working
272#if FALSE
273int 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)
274{
275 const dt_iop_roi_t *const roi_in = &piece->roi_in;
276 const dt_iop_roi_t *const roi_out = &piece->roi_out;
279
280 cl_int err = -999;
281 const int devid = pipe->devid;
282
283 const int width = roi_in->width;
284 const int height = roi_in->height;
285 const int channels = piece->dsc_in.channels;
286
287 const float radius_1 = fmax(0.1f, d->radius_1);
288 const float sigma = radius_1 * roi_in->scale;
289 const float saturation = d->saturation;
290 const int order = d->order;
291 const int unbound = d->unbound;
292
293 cl_mem dev_cm = NULL;
294 cl_mem dev_ccoeffs = NULL;
295 cl_mem dev_lm = NULL;
296 cl_mem dev_lcoeffs = NULL;
297 cl_mem dev_tmp = NULL;
298
299 dt_gaussian_cl_t *g = NULL;
300 dt_bilateral_cl_t *b = NULL;
301
302 float RGBmax[] = { 100.0f, 128.0f, 128.0f, 1.0f };
303 float RGBmin[] = { 0.0f, -128.0f, -128.0f, 0.0f };
304
305 if(unbound)
306 {
307 for(int k = 0; k < 4; k++) RGBmax[k] = INFINITY;
308 for(int k = 0; k < 4; k++) RGBmin[k] = -INFINITY;
309 }
310
311 if(d->lowpass_algo == LOWPASS_ALGO_GAUSSIAN)
312 {
313 g = dt_gaussian_init_cl(devid, width, height, channels, RGBmax, RGBmin, sigma, order);
314 if(IS_NULL_PTR(g)) goto error;
315 err = dt_gaussian_blur_cl(g, dev_in, dev_out);
316 if(err != CL_SUCCESS) goto error;
318 g = NULL;
319 }
320 else
321 {
322 const float sigma_r = 100.0f; // does not depend on scale
323 const float sigma_s = sigma;
324 const float detail = -1.0f; // we want the bilateral base layer
325
327 if(IS_NULL_PTR(b)) goto error;
328 err = dt_bilateral_splat_cl(b, dev_in);
329 if(err != CL_SUCCESS) goto error;
330 err = dt_bilateral_blur_cl(b);
331 if(err != CL_SUCCESS) goto error;
332 err = dt_bilateral_slice_cl(b, dev_in, dev_out, detail);
333 if(err != CL_SUCCESS) goto error;
335 b = NULL; // make sure we don't clean it up twice
336 }
337
338 dev_tmp = dt_opencl_alloc_device(devid, width, height, 4 * sizeof(float));
339 if(IS_NULL_PTR(dev_tmp)) goto error;
340
341 dev_cm = dt_opencl_copy_host_to_device(devid, d->ctable, 256, 256, sizeof(float));
342 if(IS_NULL_PTR(dev_cm)) goto error;
343
344 dev_ccoeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3, d->cunbounded_coeffs);
345 if(IS_NULL_PTR(dev_ccoeffs)) goto error;
346
347 dev_lm = dt_opencl_copy_host_to_device(devid, d->ltable, 256, 256, sizeof(float));
348 if(IS_NULL_PTR(dev_lm)) goto error;
349
350 dev_lcoeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3, d->lunbounded_coeffs);
351 if(IS_NULL_PTR(dev_lcoeffs)) goto error;
352
353 size_t origin[] = { 0, 0, 0 };
354 size_t region[] = { width, height, 1 };
355 err = dt_opencl_enqueue_copy_image(devid, dev_out, dev_tmp, origin, origin, region);
356 if(err != CL_SUCCESS) goto error;
357
358 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
359 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 0, sizeof(cl_mem), (void *)&dev_tmp);
360 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 1, sizeof(cl_mem), (void *)&dev_out);
361 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 2, sizeof(int), (void *)&width);
362 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 3, sizeof(int), (void *)&height);
363 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 4, sizeof(float), (void *)&saturation);
364 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 5, sizeof(cl_mem), (void *)&dev_cm);
365 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 6, sizeof(cl_mem), (void *)&dev_ccoeffs);
366 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 7, sizeof(cl_mem), (void *)&dev_lm);
367 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 8, sizeof(cl_mem), (void *)&dev_lcoeffs);
368 dt_opencl_set_kernel_arg(devid, gd->kernel_lowpass_mix, 9, sizeof(int), (void *)&unbound);
369
370 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lowpass_mix, sizes);
371 if(err != CL_SUCCESS) goto error;
372
374 dt_opencl_release_mem_object(dev_lcoeffs);
376 dt_opencl_release_mem_object(dev_ccoeffs);
378
379 return TRUE;
380
381error:
383 if(b) dt_bilateral_free_cl(b);
384
386 dt_opencl_release_mem_object(dev_lcoeffs);
388 dt_opencl_release_mem_object(dev_ccoeffs);
390 dt_print(DT_DEBUG_OPENCL, "[opencl_lowpass] couldn't enqueue kernel! %d\n", err);
391 return FALSE;
392}
393
394void 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)
395{
396 const dt_iop_roi_t *const roi_in = &piece->roi_in;
397 const dt_iop_roi_t *const roi_out = &piece->roi_out;
398 tiling->factor = 3.0f;
399 tiling->factor_cl = 5.0f;
400 tiling->maxbuf = 1.0f;
401 tiling->maxbuf_cl = 1.0f;
402 tiling->overhead = 0;
403 tiling->overlap = 0;
404 tiling->xalign = 1;
405 tiling->yalign = 1;
406}
407
409{
410 const int program = 6; // gaussian.cl, from programs.conf
413 module->data = gd;
414 gd->kernel_lowpass_mix = dt_opencl_create_kernel(program, "lowpass_mix");
415}
416
418{
421 dt_free(module->data);
422}
423
424#endif
425
426void gui_init(struct dt_iop_module_t *self)
427{
429
430 g->radius_1 = dt_bauhaus_slider_from_params(self, N_("radius_1"));
431
432 g->pixelate = dt_bauhaus_slider_from_params(self, N_("pixelate"));
433
434 g->radius_2 = dt_bauhaus_slider_from_params(self, N_("radius_2"));
435
436 g->noise = dt_bauhaus_slider_from_params(self, N_("noise"));
437
438 gtk_widget_set_tooltip_text(g->radius_1, _("radius of gaussian blur before pixellation"));
439 gtk_widget_set_tooltip_text(g->radius_2, _("radius of gaussian blur after pixellation"));
440 gtk_widget_set_tooltip_text(g->pixelate, _("radius of the intermediate pixellation"));
441 gtk_widget_set_tooltip_text(g->noise, _("amount of noise to add at the end"));
442}
443
444// clang-format off
445// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
446// vim: shiftwidth=2 expandtab tabstop=2 cindent
447// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
448// clang-format on
void cleanup_global(dt_iop_module_so_t *module)
Definition ashift.c:5680
void init_global(dt_iop_module_so_t *module)
Definition ashift.c:5668
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 ashift.c:3289
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
float sigma_s
Definition bilateral.h:3
int height
Definition bilateral.h:1
float sigma_r
Definition bilateral.h:3
void dt_bilateral_free_cl(dt_bilateral_cl_t *b)
Definition bilateralcl.c:52
cl_int dt_bilateral_slice_cl(dt_bilateral_cl_t *b, cl_mem in, cl_mem out, const float detail)
dt_bilateral_cl_t * dt_bilateral_init_cl(const int devid, const int width, const int height, const float sigma_s, const float sigma_r)
Definition bilateralcl.c:83
cl_int dt_bilateral_blur_cl(dt_bilateral_cl_t *b)
cl_int dt_bilateral_splat_cl(dt_bilateral_cl_t *b, cl_mem in)
const char ** description(struct dt_iop_module_t *self)
Definition censorize.c:88
int default_group()
Definition censorize.c:102
__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 censorize.c:140
static __DT_CLONE_TARGETS__ void make_noise(float *const output, const float noise, const size_t width, const size_t height)
Definition censorize.c:113
const char * name()
Definition censorize.c:83
void gui_init(struct dt_iop_module_t *self)
Definition censorize.c:426
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition censorize.c:107
int flags()
Definition censorize.c:97
dt_iop_censorize_params_t dt_iop_censorize_data_t
Definition censorize.c:70
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
static dt_aligned_pixel_t RGB
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_OPENCL
Definition darktable.h:722
#define DT_ALIGNED_ARRAY
Definition darktable.h:388
#define DT_IS_ALIGNED(x)
Definition darktable.h:371
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
Definition darktable.h:442
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define for_four_channels(_var,...)
Definition darktable.h:664
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
#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
static unsigned int splitmix32(const unsigned long seed)
static float xoshiro128plus(uint state[4])
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:118
void dt_gaussian_free(dt_gaussian_t *g)
Definition gaussian.c:330
void dt_gaussian_free_cl(dt_gaussian_cl_t *g)
Definition gaussian.c:353
cl_int dt_gaussian_blur_cl(dt_gaussian_cl_t *g, cl_mem dev_in, cl_mem dev_out)
Definition gaussian.c:441
void dt_gaussian_blur_4c(dt_gaussian_t *g, const float *const in, float *const out)
Definition gaussian.c:325
dt_gaussian_cl_t * dt_gaussian_init_cl(const int devid, const int width, const int height, const int channels, const float *max, const float *min, const float sigma, const int order)
Definition gaussian.c:364
dt_gaussian_t * dt_gaussian_init(const int width, const int height, const int channels, const float *max, const float *min, const float sigma, const int order)
Definition gaussian.c:122
static __DT_CLONE_TARGETS__ void dt_simd_memcpy(const float *const __restrict__ in, float *const __restrict__ out, const size_t num_elem)
Definition imagebuf.h:68
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
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
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ 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
static const float x
@ LOWPASS_ALGO_GAUSSIAN
Definition lowpass.c:76
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
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
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
Definition opencl.c:2261
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_copy_host_to_device(const int devid, void *host, const int width, const int height, const int bpp)
Definition opencl.c:2347
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
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float sigma
const float noise
struct dt_iop_module_t *void * data
dt_iop_global_data_t * data
Definition imageop.h:233
Region of interest passed through the pixelpipe.
Definition imageop.h:72
float y
Definition colorchart.h:33
float x
Definition colorchart.h:33
size_t x
Definition censorize.c:79
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 atrous.c:645