Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
dwt.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2017 Edgardo Hoszowski.
4 Copyright (C) 2019, 2021 Andreas Schneider.
5 Copyright (C) 2019, 2025-2026 Aurélien PIERRE.
6 Copyright (C) 2019 luzpaz.
7 Copyright (C) 2020 Heiko Bauke.
8 Copyright (C) 2020-2021 Hubert Kowalski.
9 Copyright (C) 2020 Pascal Obry.
10 Copyright (C) 2020-2021 Ralf Brown.
11 Copyright (C) 2022 Hanno Schwalm.
12 Copyright (C) 2022 Martin Bařinka.
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#include "system/macros.h"
29#include "system/openmp.h"
31#include "system/mem_alloc.h"
32#include "system/simd.h"
34#include "common/imagebuf.h"
35#include "dwt.h"
36
37/* Based on the original source code of GIMP's Wavelet Decompose plugin, by Marco Rossini
38 *
39 * http://registry.gimp.org/node/11742
40 *
41*/
42
43dwt_params_t *dt_dwt_init(float *image, const int width, const int height, const int ch, const int scales,
44 const int return_layer, const int merge_from_scale, void *user_data,
45 const float preview_scale, const int use_sse)
46{
47 dwt_params_t *p = (dwt_params_t *)malloc(sizeof(dwt_params_t));
48 if(IS_NULL_PTR(p)) return NULL;
49
50 p->image = image;
51 p->ch = ch;
52 p->width = width;
53 p->height = height;
54 p->scales = scales;
55 p->return_layer = return_layer;
56 p->merge_from_scale = merge_from_scale;
57 p->user_data = user_data;
58 p->preview_scale = preview_scale;
59 p->use_sse = use_sse;
60
61 return p;
62}
63
65{
66 if(IS_NULL_PTR(p)) return;
67
68 dt_free(p);
69}
70
72static int _get_max_scale(const int width, const int height, const float preview_scale)
73{
74 int maxscale = 0;
75
76 // smallest edge must be higher than or equal to 2^scales
77 unsigned int size = MIN(width, height);
78 float size_tmp = ((size >>= 1) * preview_scale);
79 while(size_tmp > 0.f)
80 {
81 size_tmp = ((size >>= 1) * preview_scale);
82 maxscale++;
83 }
84
85 // avoid rounding issues...
86 size = MIN(width, height);
87 while((maxscale > 0) && ((1 << maxscale) * preview_scale >= size)) maxscale--;
88
89 return maxscale;
90}
91
93{
94 return _get_max_scale(p->width / p->preview_scale, p->height / p->preview_scale, p->preview_scale);
95}
96
98static int _first_scale_visible(const int num_scales, const float preview_scale)
99{
100 int first_scale = 0;
101
102 for(unsigned int lev = 0; lev < num_scales; lev++)
103 {
104 int sc = 1 << lev;
105 sc *= preview_scale;
106 if(sc > 0)
107 {
108 first_scale = lev + 1;
109 break;
110 }
111 }
112
113 return first_scale;
114}
115
117{
118 return _first_scale_visible(p->scales, p->preview_scale);
119}
120
121static inline __attribute__((always_inline)) void dwt_get_image_layer(float *const layer, dwt_params_t *const p)
122{
123 if(p->image != layer) memcpy(p->image, layer, sizeof(float) * p->width * p->height * p->ch);
124}
125
126// first, "vertical" pass of wavelet decomposition
128static void dwt_decompose_vert(float *const restrict out, const float *const restrict in,
129 const size_t height, const size_t width, const size_t lev)
130{
131 const size_t vscale = MIN(1 << lev, height-1);
133 for(int rowid = 0; rowid < height ; rowid++)
134 {
135 const size_t row = dwt_interleave_rows(rowid,height,vscale);
136 // perform a weighted sum of the current pixel row with the rows 'scale' pixels above and below
137 // if either of those is beyond the edge of the image, we use reflection to get a value for averaging,
138 // i.e. we move as many rows in from the edge as we would have been beyond the edge
139 // for the top edge, this means we can simply use the absolute value of row-vscale; for the bottom edge,
140 // we need to reflect around height
141 const size_t rowstart = (size_t)4 * row * width;
142 const size_t above_row = (row > vscale) ? row - vscale : vscale - row;
143 const size_t below_row = (row + vscale < height) ? (row + vscale) : 2*(height-1) - (row + vscale);
144 const float* const restrict center = in + rowstart;
145 const float* const restrict above = in + 4 * above_row * width;
146 const float* const restrict below = in + 4 * below_row * width;
147 float* const restrict temprow = out + rowstart;
148 for (size_t col = 0; col < 4*width; col += 4)
149 {
150 for_each_channel(c,aligned(center, above, below, temprow : 16))
151 {
152 temprow[col + c] = 2.f * center[col+c] + above[col+c] + below[col+c];
153 }
154 }
155 }
156}
157
158// second, horizontal pass of wavelet decomposition; generates 'coarse' into the output buffer and overwrites
159// the input buffer with 'details'
161static void dwt_decompose_horiz(float *const restrict out, float *const restrict in, float *const temp,
162 const size_t height, const size_t width, const size_t lev)
163{
164 const int hscale = MIN(1 << lev, width); //(int because we need a signed difference below)
166 for(int row = 0; row < height ; row++)
167 {
168 // perform a weighted sum of the current pixel with the ones 'scale' pixels to the left and right, using
169 // reflection to get a value if either of those positions is out of bounds, i.e. we move as many columns
170 // in from the edge as we would have been beyond the edge to avoid an additional pass, we also rescale the
171 // final sum and split the original input into 'coarse' and 'details' by subtracting the scaled sum from
172 // the original input.
173 const size_t rowindex = (size_t)4 * (row * width);
174 float* const restrict temprow = temp + width * dt_get_thread_num() * 4;
175 float* const restrict details = in + rowindex;
176 float* const restrict coarse = out + rowindex;
177
178 for (int col = 0; col < width - hscale; col++)
179 {
180 const size_t leftpos = (size_t)4*abs(col-hscale); // the abs() handles reflection at the left edge
181 const size_t rightpos = (size_t)4*(col+hscale);
182 for_each_channel(c,aligned(temprow, details, coarse : 16))
183 {
184 const float left = coarse[leftpos+c];
185 const float right = coarse[rightpos+c];
186 // add up left/center/right, and renormalize by dividing by the total weight of all numbers added together
187 const float hat = (2.f * coarse[4*col+c] + left + right) / 16.f;
188 // the normalized value is our 'coarse' result; 'details' is the difference between original input and 'coarse'
189 temprow[4*col+c] = hat;
190 details[4*col+c] -= hat;
191 }
192 }
193 // handle reflection at right edge
194 for (int col = width - hscale; col < width; col++)
195 {
196 const size_t leftpos = (size_t)4 * abs(col-hscale); // still need to handle reflection, if hscale>=width/2
197 const size_t rightpos = (size_t)4 * (2*width - 2 - (col+hscale));
198 for_each_channel(c,aligned(temprow, details, coarse : 16))
199 {
200 const float left = coarse[leftpos+c];
201 const float right = coarse[rightpos+c];
202 // add up left/center/right, and renormalize by dividing by the total weight of all numbers added together
203 const float hat = (2.f * coarse[4*col+c] + left + right) / 16.f;
204 // the normalized value is our 'coarse' result; 'details' is the difference between original input and 'coarse'
205 temprow[4*col+c] = hat;
206 details[4*col+c] -= hat;
207 }
208 }
209 // now that we're done with the row of pixels, we can overwrite the intermediate result from the
210 // first pass with the final decomposition
211 memcpy(coarse, temprow, sizeof(float) * 4 * width);
212 }
213}
214
215// split input into 'coarse' and 'details'; put 'details' back into the input buffer
216static inline __attribute__((always_inline)) void dwt_decompose_layer(float *const restrict out, float *const restrict in, float *const temp, const int lev,
217 const dwt_params_t *const p)
218{
219 dwt_decompose_vert(out, in, p->height, p->width, lev);
220 dwt_decompose_horiz(out, in, temp, p->height, p->width, lev);
221 return;
222}
223
224/* actual decomposing algorithm */
226static int dwt_wavelet_decompose(float *img, dwt_params_t *const p, _dwt_layer_func layer_func)
227{
228 float *temp = NULL;
229 float *layers = NULL;
230 float *merged_layers = NULL;
231 float *buffer[2] = { 0, 0 };
232 int bcontinue = 1;
233 int err = 0;
234 const size_t size = (size_t)p->width * p->height * p->ch;
235
236 assert(p->ch == 4);
237
238 if(layer_func && layer_func(img, p, 0) != 0) return 1;
239
240 if(p->scales <= 0) goto cleanup;
241
242 /* image buffers */
243 buffer[0] = img;
244 /* temporary storage */
246 // buffer to reconstruct the image
247 layers = dt_pixelpipe_cache_alloc_align_float_cache((size_t)4 * p->width * p->height, 0);
248 // scratch buffer for decomposition
250
251 if(buffer[1] == NULL || IS_NULL_PTR(layers) || IS_NULL_PTR(temp))
252 {
253 printf("not enough memory for wavelet decomposition");
254 err = 1;
255 goto cleanup;
256 }
257 dt_iop_image_fill(layers,0.0f,p->width,p->height,p->ch);
258
259 if(p->merge_from_scale > 0)
260 {
261 merged_layers = dt_pixelpipe_cache_alloc_align_float_cache((size_t)p->width * p->height * p->ch, 0);
262 if(IS_NULL_PTR(merged_layers))
263 {
264 printf("not enough memory for wavelet decomposition");
265 err = 1;
266 goto cleanup;
267 }
268 dt_iop_image_fill(merged_layers,0.0f,p->width,p->height,p->ch);
269 }
270
271 // iterate over wavelet scales
272 unsigned int hpass = 0;
273 for(unsigned int lev = 0; lev < p->scales && bcontinue; lev++)
274 {
275 unsigned int lpass = (1 - (lev & 1));
276
277 dwt_decompose_layer(buffer[lpass], buffer[hpass], temp, lev, p);
278
279 // no merge scales or we didn't reach the merge scale from yet
280 if(p->merge_from_scale == 0 || p->merge_from_scale > lev + 1)
281 {
282 // allow to process this detail scale
283 if(layer_func && layer_func(buffer[hpass], p, lev + 1) != 0)
284 {
285 err = 1;
286 goto cleanup;
287 }
288
289 // user wants to preview this detail scale
290 if(p->return_layer == lev + 1)
291 {
292 // return this detail scale
293 dwt_get_image_layer(buffer[hpass], p);
294
295 bcontinue = 0;
296 }
297 // user wants the entire reconstructed image
298 else if(p->return_layer == 0)
299 {
300 // add this detail scale to the final image
301 dt_iop_image_add_image(layers, buffer[hpass], p->width, p->height, p->ch);
302 }
303 }
304 // we are on the merge scales range
305 else
306 {
307 // add this detail scale to the merged ones
308 dt_iop_image_add_image(merged_layers, buffer[hpass], p->width, p->height, p->ch);
309
310 // allow to process this merged scale
311 if(layer_func && layer_func(merged_layers, p, lev + 1) != 0)
312 {
313 err = 1;
314 goto cleanup;
315 }
316
317 // user wants to preview this merged scale
318 if(p->return_layer == lev + 1)
319 {
320 // return this merged scale
321 dwt_get_image_layer(merged_layers, p);
322
323 bcontinue = 0;
324 }
325 }
326
327 hpass = lpass;
328 }
329
330 // all scales have been processed
331 if(bcontinue)
332 {
333 // allow to process residual image
334 if(layer_func && layer_func(buffer[hpass], p, p->scales + 1) != 0)
335 {
336 err = 1;
337 goto cleanup;
338 }
339
340 // user wants to preview residual image
341 if(p->return_layer == p->scales + 1)
342 {
343 // return residual image
344 dwt_get_image_layer(buffer[hpass], p);
345 }
346 // return reconstructed image
347 else if(p->return_layer == 0)
348 {
349 // some of the detail scales are on the merged layers
350 if(p->merge_from_scale > 0)
351 {
352 // add merged layers to final image
353 dt_iop_image_add_image(layers, merged_layers, p->width, p->height, p->ch);
354 }
355
356 // add residual image to final image
357 dt_iop_image_add_image(layers, buffer[hpass], p->width, p->height, p->ch);
358
359 // allow to process reconstructed image
360 if(layer_func && layer_func(layers, p, p->scales + 2) != 0)
361 {
362 err = 1;
363 goto cleanup;
364 }
365
366 // return reconstructed image
367 dwt_get_image_layer(layers, p);
368 }
369 }
370
371cleanup:
374 dt_pixelpipe_cache_free_align(merged_layers);
376 return err;
377}
378
379/* this function prepares for decomposing, which is done in the function dwt_wavelet_decompose() */
381{
382 // this is a zoom scale, not a wavelet scale
383 if(p->preview_scale <= 0.f) p->preview_scale = 1.f;
384
385 // if a single scale is requested it cannot be grather than the residual
386 if(p->return_layer > p->scales + 1)
387 {
388 p->return_layer = p->scales + 1;
389 }
390
391 const int max_scale = dwt_get_max_scale(p);
392
393 // if requested scales is grather than max scales adjust it
394 if(p->scales > max_scale)
395 {
396 // residual should be returned
397 if(p->return_layer > p->scales) p->return_layer = max_scale + 1;
398 // a scale should be returned, it cannot be grather than max scales
399 else if(p->return_layer > max_scale)
400 p->return_layer = max_scale;
401
402 p->scales = max_scale;
403 }
404
405 // call the actual decompose
406 return dwt_wavelet_decompose(p->image, p, layer_func);
407}
408
409// first, "vertical" pass of wavelet decomposition
411static void dwt_denoise_vert_1ch(float *const restrict out, const float *const restrict in,
412 const size_t height, const size_t width, const size_t lev)
413{
414 const int vscale = MIN(1 << lev, height);
416 for(int rowid = 0; rowid < height ; rowid++)
417 {
418 const int row = dwt_interleave_rows(rowid,height,vscale);
419 // perform a weighted sum of the current pixel row with the rows 'scale' pixels above and below
420 // if either of those is beyond the edge of the image, we use reflection to get a value for averaging,
421 // i.e. we move as many rows in from the edge as we would have been beyond the edge
422 // for the top edge, this means we can simply use the absolute value of row-vscale; for the bottom edge,
423 // we need to reflect around height
424 const size_t rowstart = (size_t)row * width;
425 const size_t below_row = (row + vscale < height) ? (row + vscale) : 2*(height-1) - (row + vscale);
426 const float *const restrict center = in + rowstart;
427 const float *const restrict above = in + abs(row - vscale) * width;
428 const float *const restrict below = in + below_row * width;
429 float* const restrict outrow = out + rowstart;
431 for (int col= 0; col < width; col++)
432 {
433 outrow[col] = 2.f * center[col] + above[col] + below[col];
434 }
435 }
436}
437
438// second, horizontal pass of wavelet decomposition; generates 'coarse' into the output buffer and overwrites
439// the input buffer with 'details'
441static void dwt_denoise_horiz_1ch(float *const restrict out, float *const restrict in,
442 float *const restrict accum, const size_t height, const size_t width,
443 const size_t lev, const float thold, const int last)
444{
445 const int hscale = MIN(1 << lev, width);
447 for(int row = 0; row < height ; row++)
448 {
449 // perform a weighted sum of the current pixel with the ones 'scale' pixels to the left and right, using
450 // reflection to get a value if either of those positions is out of bounds, i.e. we move as many columns
451 // in from the edge as we would have been beyond the edge to avoid an additional pass, we also rescale the
452 // final sum and split the original input into 'coarse' and 'details' by subtracting the scaled sum from
453 // the original input.
454 const size_t rowindex = (size_t)row * width;
455 float *const restrict details = in + rowindex;
456 float *const restrict coarse = out + rowindex;
457 float *const restrict accum_row = accum + rowindex;
458 // handle reflection at left edge
460 for (int col = 0; col < hscale; col++)
461 {
462 // add up left/center/right, and renormalize by dividing by the total weight of all numbers added together
463 const float hat = (2.f * coarse[col] + coarse[hscale-col] + coarse[col+hscale]) / 16.f;
464 // the normalized value is our 'coarse' result; 'diff' is the difference between original input and 'coarse'
465 // (which would ordinarily be stored as the details scale, but we don't need it any further)
466 const float diff = details[col] - hat;
467 details[col] = hat; // done with original input, so we can overwrite it with 'coarse'
468 // GCC8 won't vectorize if we use the following line, but it turns out that just adding the two conditional
469 // alternatives produces exactly the same result, and *that* does get vectorized
470 //const float excess = diff < 0.0 ? MIN(diff + thold, 0.0f) : MAX(diff - thold, 0.0f);
471 accum_row[col] += MAX(diff - thold,0.0f) + MIN(diff + thold, 0.0f);
472 }
474 for (int col = hscale; col < width - hscale; col++)
475 {
476 // add up left/center/right, and renormalize by dividing by the total weight of all numbers added together
477 const float hat = (2.f * coarse[col] + coarse[col-hscale] + coarse[col+hscale]) / 16.f;
478 // the normalized value is our 'coarse' result; 'diff' is the difference between original input and 'coarse'
479 // (which would ordinarily be stored as the details scale, but we don't need it any further)
480 const float diff = details[col] - hat;
481 details[col] = hat; // done with original input, so we can overwrite it with 'coarse'
482 // GCC8 won't vectorize if we use the following line, but it turns out that just adding the two conditional
483 // alternatives produces exactly the same result, and *that* does get vectorized
484 //const float excess = diff < 0.0 ? MIN(diff + thold, 0.0f) : MAX(diff - thold, 0.0f);
485 accum_row[col] += MAX(diff - thold,0.0f) + MIN(diff + thold, 0.0f);
486 }
487 // handle reflection at right edge
489 for (int col = width - hscale; col < width; col++)
490 {
491 const float right = coarse[2*width - 2 - (col+hscale)];
492 // add up left/center/right, and renormalize by dividing by the total weight of all numbers added together
493 const float hat = (2.f * coarse[col] + coarse[col-hscale] + right) / 16.f;
494 // the normalized value is our 'coarse' result; 'diff' is the difference between original input and 'coarse'
495 // (which would ordinarily be stored as the details scale, but we don't need it any further)
496 const float diff = details[col] - hat;
497 details[col] = hat; // done with original input, so we can overwrite it with 'coarse'
498 accum_row[col] += MAX(diff - thold,0.0f) + MIN(diff + thold, 0.0f);
499 }
500 if (last)
501 {
502 // add the details to the residue to create the final denoised result
503 for (int col = 0; col < width; col++)
504 {
505 details[col] += accum_row[col];
506 }
507 }
508 }
509}
510
511/* this function denoises an image by decomposing it into the specified number of wavelet scales and
512 * recomposing the result from just the portion of each scale which exceeds the magnitude of the given
513 * threshold for that scale.
514 */
516int dwt_denoise(float *const img, const int width, const int height, const int bands, const float *const noise)
517{
518 float *const details = dt_pixelpipe_cache_alloc_align_float_cache((size_t)2 * width * height, 0);
519 if(IS_NULL_PTR(details)) return 1;
520 float *const interm = details + width * height; // temporary storage for use during each pass
521
522 // zero the accumulator
523 dt_iop_image_fill(details, 0.0f, width, height, 1);
524
525 for(int lev = 0; lev < bands; lev++)
526 {
527 const int last = (lev+1) == bands;
528
529 // "vertical" pass, averages pixels with those 'scale' rows above and below and puts result in 'interm'
530 dwt_denoise_vert_1ch(interm, img, height, width, lev);
531 // horizontal filtering pass, averages pixels in 'interm' with those 'scale' rows to the left and right
532 // accumulates the portion of the detail scale that is above the noise threshold into 'details'; this
533 // will be added to the residue left in 'img' on the last iteration
534 dwt_denoise_horiz_1ch(interm, img, details, height, width, lev, noise[lev], last);
535 }
537 return 0;
538}
539
540#ifdef HAVE_OPENCL
541/* The kernels this subsystem compiles, owned HERE. They used to be handed to
542 * common/opencl.c, parked on the application-wide dt_opencl_t, and read back from it --
543 * a round trip through a god-struct that added nothing but an ordering. opencl.c still
544 * calls init/free, because the kernels must be built after the devices exist, but the
545 * pointer never leaves this file. */
547
549{
551
552 const int program = 20; // dwt.cl, from programs.conf
553 g->kernel_dwt_add_img_to_layer = dt_opencl_create_kernel(program, "dwt_add_img_to_layer");
554 g->kernel_dwt_subtract_layer = dt_opencl_create_kernel(program, "dwt_subtract_layer");
555 g->kernel_dwt_hat_transform_col = dt_opencl_create_kernel(program, "dwt_hat_transform_col");
556 g->kernel_dwt_hat_transform_row = dt_opencl_create_kernel(program, "dwt_hat_transform_row");
557 g->kernel_dwt_init_buffer = dt_opencl_create_kernel(program, "dwt_init_buffer");
559}
560
562{
564 _dwt_cl_global = NULL;
565 if(IS_NULL_PTR(g)) return;
566
567 // destroy kernels
568 dt_opencl_free_kernel(g->kernel_dwt_add_img_to_layer);
569 dt_opencl_free_kernel(g->kernel_dwt_subtract_layer);
570 dt_opencl_free_kernel(g->kernel_dwt_hat_transform_col);
571 dt_opencl_free_kernel(g->kernel_dwt_hat_transform_row);
572 dt_opencl_free_kernel(g->kernel_dwt_init_buffer);
573
574 dt_free(g);
575}
576
577dwt_params_cl_t *dt_dwt_init_cl(const int devid, cl_mem image, const int width, const int height, const int scales,
578 const int return_layer, const int merge_from_scale, void *user_data,
579 const float preview_scale)
580{
581 dwt_params_cl_t *p = (dwt_params_cl_t *)malloc(sizeof(dwt_params_cl_t));
582 if(IS_NULL_PTR(p)) return NULL;
583
585 p->devid = devid;
586 p->image = image;
587 p->ch = 4;
588 p->width = width;
589 p->height = height;
590 p->scales = scales;
591 p->return_layer = return_layer;
592 p->merge_from_scale = merge_from_scale;
593 p->user_data = user_data;
594 p->preview_scale = preview_scale;
595
596 return p;
597}
598
600{
601 if(IS_NULL_PTR(p)) return;
602 dt_free(p);
603}
604
606{
607 return _get_max_scale(p->width / p->preview_scale, p->height / p->preview_scale, p->preview_scale);
608}
609
611{
612 return _first_scale_visible(p->scales, p->preview_scale);
613}
614
615static cl_int dwt_subtract_layer_cl(cl_mem bl, cl_mem bh, dwt_params_cl_t *const p)
616{
617 cl_int err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
618
619 const int devid = p->devid;
620 const int kernel = p->global->kernel_dwt_subtract_layer;
621
622 size_t sizes[] = { ROUNDUPDWD(p->width, devid), ROUNDUPDHT(p->height, devid), 1 };
623
624 const float lpass_mult = (1.f / 16.f);
625 const int width = p->width;
626 const int height = p->height;
627
628 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&bl);
629 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&bh);
630 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(width));
631 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(height));
632 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(float), (void *)&lpass_mult);
633 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
634
635 return err;
636}
637
638static cl_int dwt_add_layer_cl(cl_mem img, cl_mem layers, dwt_params_cl_t *const p, const int n_scale)
639{
640 cl_int err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
641
642 const int devid = p->devid;
643 const int kernel = p->global->kernel_dwt_add_img_to_layer;
644
645 size_t sizes[] = { ROUNDUPDWD(p->width, devid), ROUNDUPDHT(p->height, devid), 1 };
646
647 const int width = p->width;
648 const int height = p->height;
649
650 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&img);
651 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&layers);
652 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(width));
653 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(height));
654 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
655
656 return err;
657}
658
659static cl_int dwt_get_image_layer_cl(cl_mem layer, dwt_params_cl_t *const p)
660{
661 cl_int err = CL_SUCCESS;
662
663 if(p->image != layer)
664 err = dt_opencl_enqueue_copy_buffer_to_buffer(p->devid, layer, p->image, 0, 0,
665 (size_t)p->width * p->height * p->ch * sizeof(float));
666
667 return err;
668}
669
670static cl_int dwt_wavelet_decompose_cl(cl_mem img, dwt_params_cl_t *const p, _dwt_layer_func_cl layer_func)
671{
672 cl_int err = CL_SUCCESS;
673
674 const int devid = p->devid;
675
676 cl_mem temp = NULL;
677 cl_mem layers = NULL;
678 cl_mem merged_layers = NULL;
679 unsigned int lpass, hpass;
680 cl_mem buffer[2] = { 0, 0 };
681 int bcontinue = 1;
682
683 if(layer_func)
684 {
685 err = layer_func(img, p, 0);
686 if(err != CL_SUCCESS) goto cleanup;
687 }
688
689 if(p->scales <= 0) goto cleanup;
690
691 /* image buffers */
692 buffer[0] = img;
693 /* temporary storage */
694 buffer[1] = dt_opencl_alloc_device_buffer(devid, sizeof(float) * p->ch * p->width * p->height);
695 if(buffer[1] == NULL)
696 {
697 printf("not enough memory for wavelet decomposition");
698 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
699 goto cleanup;
700 }
701
702 // buffer to reconstruct the image
703 layers = dt_opencl_alloc_device_buffer(devid, sizeof(float) * p->ch * p->width * p->height);
704 if(IS_NULL_PTR(layers))
705 {
706 printf("not enough memory for wavelet decomposition");
707 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
708 goto cleanup;
709 }
710 // init layer buffer
711 {
712 const int kernel = p->global->kernel_dwt_init_buffer;
713
714 size_t sizes[] = { ROUNDUPDWD(p->width, devid), ROUNDUPDHT(p->height, devid), 1 };
715 const int width = p->width;
716 const int height = p->height;
717
718 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&layers);
719 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(width));
720 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(height));
721 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
722 if(err != CL_SUCCESS) goto cleanup;
723 }
724
725 if(p->merge_from_scale > 0)
726 {
727 merged_layers = dt_opencl_alloc_device_buffer(devid, sizeof(float) * p->ch * p->width * p->height);
728 if(IS_NULL_PTR(merged_layers))
729 {
730 printf("not enough memory for wavelet decomposition");
731 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
732 goto cleanup;
733 }
734 // init reconstruct buffer
735 {
736 const int kernel = p->global->kernel_dwt_init_buffer;
737
738 size_t sizes[] = { ROUNDUPDWD(p->width, devid), ROUNDUPDHT(p->height, devid), 1 };
739 const int width = p->width;
740 const int height = p->height;
741
742 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&merged_layers);
743 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(width));
744 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(height));
745 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
746 if(err != CL_SUCCESS) goto cleanup;
747 }
748 }
749
750 // iterate over wavelet scales
751 lpass = 1;
752 hpass = 0;
753 for(unsigned int lev = 0; lev < p->scales && bcontinue; lev++)
754 {
755 lpass = (1 - (lev & 1));
756
757 // when (*layer_func) uses too much memory I get a -4 error, so alloc and free for each scale
758 // setup a temp buffer
759 temp = dt_opencl_alloc_device_buffer(devid, sizeof(float) * p->ch * p->width * p->height);
760 if(IS_NULL_PTR(temp))
761 {
762 printf("not enough memory for wavelet decomposition");
763 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
764 goto cleanup;
765 }
766
767 // hat transform by row
768 {
769 const int kernel = p->global->kernel_dwt_hat_transform_row;
770
771 int sc = 1 << lev;
772 sc = (int)(sc * p->preview_scale);
773 if(sc > p->width) sc = p->width;
774
775 size_t sizes[] = { ROUNDUPDWD(p->width, devid), ROUNDUPDHT(p->height, devid), 1 };
776
777 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&temp);
778 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&(buffer[hpass]));
779 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(p->width));
780 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(p->height));
781 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), (void *)&sc);
782 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
783 if(err != CL_SUCCESS) goto cleanup;
784 }
785
786 // hat transform by col
787 {
788 const int kernel = p->global->kernel_dwt_hat_transform_col;
789
790 int sc = 1 << lev;
791 sc = (int)(sc * p->preview_scale);
792 if(sc > p->height) sc = p->height;
793 const float lpass_mult = (1.f / 16.f);
794
795 size_t sizes[] = { ROUNDUPDWD(p->width, devid), ROUNDUPDHT(p->height, devid), 1 };
796
797 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&temp);
798 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(p->width));
799 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(p->height));
800 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&sc);
801 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&(buffer[lpass]));
802 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&lpass_mult);
803 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
804 if(err != CL_SUCCESS) goto cleanup;
805 }
806
807 if(temp)
808 {
810 temp = NULL;
811 }
812
813 err = dwt_subtract_layer_cl(buffer[lpass], buffer[hpass], p);
814 if(err != CL_SUCCESS) goto cleanup;
815
816 // no merge scales or we didn't reach the merge scale from yet
817 if(p->merge_from_scale == 0 || p->merge_from_scale > lev + 1)
818 {
819 // allow to process this detail scale
820 if(layer_func)
821 {
822 err = layer_func(buffer[hpass], p, lev + 1);
823 if(err != CL_SUCCESS) goto cleanup;
824 }
825
826 // user wants to preview this detail scale
827 if(p->return_layer == lev + 1)
828 {
829 // return this detail scale
830 err = dwt_get_image_layer_cl(buffer[hpass], p);
831 if(err != CL_SUCCESS) goto cleanup;
832
833 bcontinue = 0;
834 }
835 // user wants the entire reconstructed image
836 else if(p->return_layer == 0)
837 {
838 // add this detail scale to the final image
839 err = dwt_add_layer_cl(buffer[hpass], layers, p, lev + 1);
840 if(err != CL_SUCCESS) goto cleanup;
841 }
842 }
843 // we are on the merge scales range
844 else
845 {
846 // add this detail scale to the merged ones
847 err = dwt_add_layer_cl(buffer[hpass], merged_layers, p, lev + 1);
848 if(err != CL_SUCCESS) goto cleanup;
849
850 // allow to process this merged scale
851 if(layer_func)
852 {
853 err = layer_func(merged_layers, p, lev + 1);
854 if(err != CL_SUCCESS) goto cleanup;
855 }
856
857 // user wants to preview this merged scale
858 if(p->return_layer == lev + 1)
859 {
860 // return this merged scale
861 err = dwt_get_image_layer_cl(merged_layers, p);
862 if(err != CL_SUCCESS) goto cleanup;
863
864 bcontinue = 0;
865 }
866 }
867
868 hpass = lpass;
869 }
870
871 // all scales have been processed
872 if(bcontinue)
873 {
874 // allow to process residual image
875 if(layer_func)
876 {
877 err = layer_func(buffer[hpass], p, p->scales + 1);
878 if(err != CL_SUCCESS) goto cleanup;
879 }
880
881 // user wants to preview residual image
882 if(p->return_layer == p->scales + 1)
883 {
884 // return residual image
885 err = dwt_get_image_layer_cl(buffer[hpass], p);
886 if(err != CL_SUCCESS) goto cleanup;
887 }
888 // return reconstructed image
889 else if(p->return_layer == 0)
890 {
891 // some of the detail scales are on the merged layers
892 if(p->merge_from_scale > 0)
893 {
894 // add merged layers to final image
895 err = dwt_add_layer_cl(merged_layers, layers, p, p->scales + 1);
896 if(err != CL_SUCCESS) goto cleanup;
897 }
898
899 // add residual image to final image
900 err = dwt_add_layer_cl(buffer[hpass], layers, p, p->scales + 1);
901 if(err != CL_SUCCESS) goto cleanup;
902
903 // allow to process reconstructed image
904 if(layer_func)
905 {
906 err = layer_func(layers, p, p->scales + 2);
907 if(err != CL_SUCCESS) goto cleanup;
908 }
909
910 // return reconstructed image
911 err = dwt_get_image_layer_cl(layers, p);
912 if(err != CL_SUCCESS) goto cleanup;
913 }
914 }
915
916cleanup:
918 dt_opencl_release_mem_object(merged_layers);
921
922 return err;
923}
924
926{
927 cl_int err = CL_SUCCESS;
928
929 // this is a zoom scale, not a wavelet scale
930 if(p->preview_scale <= 0.f) p->preview_scale = 1.f;
931
932 // if a single scale is requested it cannot be grather than the residual
933 if(p->return_layer > p->scales + 1)
934 {
935 p->return_layer = p->scales + 1;
936 }
937
938 const int max_scale = dwt_get_max_scale_cl(p);
939
940 // if requested scales is grather than max scales adjust it
941 if(p->scales > max_scale)
942 {
943 // residual should be returned
944 if(p->return_layer > p->scales) p->return_layer = max_scale + 1;
945 // a scale should be returned, it cannot be grather than max scales
946 else if(p->return_layer > max_scale)
947 p->return_layer = max_scale;
948
949 p->scales = max_scale;
950 }
951
952 // call the actual decompose
953 err = dwt_wavelet_decompose_cl(p->image, p, layer_func);
954
955 return err;
956}
957
958#endif
959// clang-format off
960// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
961// vim: shiftwidth=2 expandtab tabstop=2 cindent
962// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
963// clang-format on
void cleanup(dt_imageio_module_format_t *self)
Definition avif.c:170
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
Definition darktable.c:518
static __DT_CLONE_TARGETS__ void dwt_decompose_horiz(float *const restrict out, float *const restrict in, float *const temp, const size_t height, const size_t width, const size_t lev)
Definition dwt.c:161
static __DT_CLONE_TARGETS__ int dwt_wavelet_decompose(float *img, dwt_params_t *const p, _dwt_layer_func layer_func)
Definition dwt.c:226
__DT_CLONE_TARGETS__ int dwt_denoise(float *const img, const int width, const int height, const int bands, const float *const noise)
Definition dwt.c:516
int dt_dwt_first_scale_visible_cl(dwt_params_cl_t *p)
Definition dwt.c:610
static cl_int dwt_wavelet_decompose_cl(cl_mem img, dwt_params_cl_t *const p, _dwt_layer_func_cl layer_func)
Definition dwt.c:670
int dwt_decompose(dwt_params_t *p, _dwt_layer_func layer_func)
Definition dwt.c:380
int dwt_get_max_scale(dwt_params_t *p)
Definition dwt.c:92
void dt_dwt_free(dwt_params_t *p)
Definition dwt.c:64
dwt_params_cl_t * dt_dwt_init_cl(const int devid, cl_mem image, const int width, const int height, const int scales, const int return_layer, const int merge_from_scale, void *user_data, const float preview_scale)
Definition dwt.c:577
static __DT_CLONE_TARGETS__ void dwt_denoise_horiz_1ch(float *const restrict out, float *const restrict in, float *const restrict accum, const size_t height, const size_t width, const size_t lev, const float thold, const int last)
Definition dwt.c:441
static __DT_CLONE_TARGETS__ void dwt_decompose_vert(float *const restrict out, const float *const restrict in, const size_t height, const size_t width, const size_t lev)
Definition dwt.c:128
static __DT_CLONE_TARGETS__ void dwt_denoise_vert_1ch(float *const restrict out, const float *const restrict in, const size_t height, const size_t width, const size_t lev)
Definition dwt.c:411
static dt_dwt_cl_global_t * _dwt_cl_global
Definition dwt.c:546
void dt_dwt_init_cl_global(void)
Definition dwt.c:548
void dt_dwt_free_cl(dwt_params_cl_t *p)
Definition dwt.c:599
dwt_params_t * dt_dwt_init(float *image, const int width, const int height, const int ch, const int scales, const int return_layer, const int merge_from_scale, void *user_data, const float preview_scale, const int use_sse)
Definition dwt.c:43
void dt_dwt_free_cl_global(void)
Definition dwt.c:561
static __DT_CLONE_TARGETS__ int _get_max_scale(const int width, const int height, const float preview_scale)
Definition dwt.c:72
cl_int dwt_decompose_cl(dwt_params_cl_t *p, _dwt_layer_func_cl layer_func)
Definition dwt.c:925
int dwt_get_max_scale_cl(dwt_params_cl_t *p)
Definition dwt.c:605
static __DT_CLONE_TARGETS__ int _first_scale_visible(const int num_scales, const float preview_scale)
Definition dwt.c:98
static cl_int dwt_get_image_layer_cl(cl_mem layer, dwt_params_cl_t *const p)
Definition dwt.c:659
int dt_dwt_first_scale_visible(dwt_params_t *p)
Definition dwt.c:116
static cl_int dwt_add_layer_cl(cl_mem img, cl_mem layers, dwt_params_cl_t *const p, const int n_scale)
Definition dwt.c:638
static cl_int dwt_subtract_layer_cl(cl_mem bl, cl_mem bh, dwt_params_cl_t *const p)
Definition dwt.c:615
int() _dwt_layer_func(float *layer, dwt_params_t *const p, const int scale)
Definition dwt.h:42
static int dwt_interleave_rows(const int rowid, const int height, const int stride)
Definition dwt.h:93
cl_int() _dwt_layer_func_cl(cl_mem layer, dwt_params_cl_t *const p, const int scale)
Definition dwt.h:134
__DT_CLONE_TARGETS__ void dt_iop_image_add_image(float *const buf, const float *const other_image, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.c:280
__DT_CLONE_TARGETS__ void dt_iop_image_fill(float *const buf, const float fill_value, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.c:218
static float kernel(const float *x, const float *y)
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_buffer(const int devid, const size_t size)
Definition opencl.c:2970
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
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_enqueue_copy_buffer_to_buffer(const int devid, cl_mem src_buffer, cl_mem dst_buffer, size_t srcoffset, size_t dstoffset, size_t size)
Definition opencl.c:2714
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_SIMD__(...)
Definition openmp.h:99
static int dt_get_thread_num()
Index of the calling thread within its parallel region, 0 outside one.
Definition openmp.h:129
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
#define for_each_channel(_var,...)
Definition simd.h:87
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
const float noise
int kernel_dwt_add_img_to_layer
Definition dwt.h:112
dt_dwt_cl_global_t * global
Definition dwt.h:121
float * image
Definition dwt.h:29
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29