Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
eaw.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020-2021 Hubert Kowalski.
4 Copyright (C) 2020-2021 Ralf Brown.
5 Copyright (C) 2021 parafin.
6 Copyright (C) 2021 Pascal Obry.
7 Copyright (C) 2022 Martin Baƙinka.
8
9 darktable is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 darktable is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with darktable. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "pixel/eaw.h"
24#include "math/math.h" // dt_fast_expf, fast_mexp2f
25#include "system/openmp.h"
26#include "system/simd.h"
27#include "pixel/dwt.h" // for dwt_interleave_rows
28
29static inline void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
30{
31 dt_aligned_pixel_t square;
32 for_each_channel(c) square[c] = c1[c] - c2[c];
33 for_each_channel(c) square[c] = square[c] * square[c];
34
35 const float wl = dt_fast_expf(-sharpen * square[0]);
36 const float wc = dt_fast_expf(-sharpen * (square[1] + square[2]));
37
38 weight[0] = wl;
39 weight[1] = wc;
40 weight[2] = wc;
41 weight[3] = 1.0f;
42}
43
44
45#define SUM_PIXEL_CONTRIBUTION(ii, jj) \
46 do \
47 { \
48 const float f = filter[(ii)] * filter[(jj)]; \
49 dt_aligned_pixel_t wp; \
50 weight(px, px2, sharpen, wp); \
51 dt_aligned_pixel_t w; \
52 dt_aligned_pixel_t pd; \
53 for_four_channels(c,aligned(px2)) \
54 { \
55 w[c] = f * wp[c]; \
56 wgt[c] += w[c]; \
57 pd[c] = w[c] * px2[c]; \
58 sum[c] += pd[c]; \
59 } \
60 } while(0)
61
62#define SUM_PIXEL_PROLOGUE \
63 dt_aligned_pixel_t sum = { 0.0f, 0.0f, 0.0f, 0.0f }; \
64 dt_aligned_pixel_t wgt = { 0.0f, 0.0f, 0.0f, 0.0f };
65
66#define SUM_PIXEL_EPILOGUE \
67 for_each_channel(c) \
68 { \
69 sum[c] /= wgt[c]; \
70 pcoarse[c] = sum[c]; \
71 const float det = (px[c] - sum[c]); \
72 pdetail[c] = det; \
73 } \
74 px += 4; \
75 pdetail += 4; \
76 pcoarse += 4;
77
78
79void eaw_decompose(float *const restrict out, const float *const restrict in, float *const restrict detail,
80 const int scale, const float sharpen, const int32_t width, const int32_t height)
81{
82 const int mult = 1 << scale;
83 static const float filter[5] = { 1.0f / 16.0f, 4.0f / 16.0f, 6.0f / 16.0f, 4.0f / 16.0f, 1.0f / 16.0f };
84 const int boundary = 2 * mult;
86 for(int rowid = 0; rowid < height; rowid++)
87 {
88 const size_t j = dwt_interleave_rows(rowid, height, mult);
89 const float *px = ((float *)in) + (size_t)4 * j * width;
90 const float *px2;
91 float *pdetail = detail + (size_t)4 * j * width;
92 float *pcoarse = out + (size_t)4 * j * width;
93
94 // for the first and last 'boundary' rows, we have to perform boundary tests for the entire row;
95 // for the central bulk, we only need to use those slower versions on the leftmost and rightmost pixels
96 const int lbound = (j < boundary || j >= height - boundary) ? width-boundary : boundary;
97
98 /* The first "2*mult" pixels need a boundary check because we might try to access past the left edge,
99 * which requires nearest pixel interpolation */
100 int i;
101 for(i = 0; i < lbound; i++)
102 {
104 for(int jj = 0; jj < 5; jj++)
105 {
106 const int y = j + mult * (jj-2);
107 const int clamp_y = CLAMP(y,0,height-1);
108 for(int ii = 0; ii < 5; ii++)
109 {
110 int x = i + mult * ((ii)-2);
111 if(x < 0) x = 0; // we might be looking past the left edge
112 px2 = ((float *)in) + 4 * x + (size_t)4 * clamp_y * width;
114 }
115 }
117 }
118
119 /* For pixels [2*mult, width-2*mult], we don't need to do any boundary checks */
120 for( ; i < width - boundary; i++)
121 {
123 px2 = ((float *)in) + (size_t)4 * (i - 2 * mult + (size_t)(j - 2 * mult) * width);
124 for(int jj = 0; jj < 5; jj++)
125 {
126 for(int ii = 0; ii < 5; ii++)
127 {
129 px2 += (size_t)4 * mult;
130 }
131 px2 += (size_t)4 * (width - 5) * mult;
132 }
134 }
135
136 /* Last 2*mult pixels in the row require the boundary check again */
137 for( ; i < width; i++)
138 {
140 for(int jj = 0; jj < 5; jj++)
141 {
142 const int y = j + mult * (jj-2);
143 const int clamp_y = CLAMP(y,0,height-1);
144 for(int ii = 0; ii < 5; ii++)
145 {
146 int x = i + mult * ((ii)-2);
147 if(x >= width) x = width - 1; // we might be looking beyond the right edge
148 px2 = ((float *)in) + 4 * x + (size_t)4 * clamp_y * width;
150 }
151 }
153 }
154 }
155}
156
157void eaw_synthesize(float *const out, const float *const in, const float *const restrict detail,
158 const float *const restrict threshold, const float *const restrict boost,
159 const int32_t width, const int32_t height)
160{
162 for(size_t k = 0; k < (size_t)width * height; k++)
163 {
164 __OMP_SIMD__(simdlen(4) aligned(detail, in, out, threshold, boost))
165 for(size_t c = 0; c < 4; c++)
166 {
167 // decrease the absolute magnitude of the detail by the threshold; copysignf does not vectorize, but it
168 // turns out that just adding up two clamped alternatives gives exactly the same result and DOES vectorize
169 //const float absamt = fmaxf(0.0f, (fabsf(detail[k + c]) - threshold[c]));
170 //const float amount = copysignf(absamt, detail[k + c]);
171 const float amount = MAX(detail[4*k+c] - threshold[c], 0.0f) + MIN(detail[4*k+c] + threshold[c], 0.0f);
172 out[4*k + c] = in[4*k + c] + (boost[c] * amount);
173 }
174 }
175}
176
177// =====================================================================================
178// begin wavelet code from denoiseprofile.c
179// =====================================================================================
180
181static inline float dn_weight(const float *c1, const float *c2, const float inv_sigma2)
182{
183 // 3d distance based on color
186 {
187 const float diff = c1[c] - c2[c];
188 sqr[c] = diff * diff;
189 }
190 const float dot = (sqr[0] + sqr[1] + sqr[2]) * inv_sigma2;
191 const float var
192 = 0.02f; // FIXME: this should ideally depend on the image before noise stabilizing transforms!
193 const float off2 = 9.0f; // (3 sigma)^2
194 return fast_mexp2f(MAX(0, dot * var - off2));
195}
196
197typedef struct _aligned_pixel {
198 union {
200 };
202#ifdef _OPENMP
203static inline _aligned_pixel add_float4(_aligned_pixel acc, _aligned_pixel newval)
204{
205 for_four_channels(c) acc.v[c] += newval.v[c];
206 return acc;
207}
208#pragma omp declare reduction(vsum:_aligned_pixel:omp_out=add_float4(omp_out,omp_in)) \
209 initializer(omp_priv = { .v = { 0.0f, 0.0f, 0.0f, 0.0f } })
210#endif
211
212#undef SUM_PIXEL_CONTRIBUTION
213#define SUM_PIXEL_CONTRIBUTION(ii, jj) \
214 do \
215 { \
216 const float f = filter[(ii)] * filter[(jj)]; \
217 const float wp = dn_weight(px, px2, inv_sigma2); \
218 const float w = f * wp; \
219 dt_aligned_pixel_t pd; \
220 for_each_channel(c,aligned(px2)) \
221 { \
222 pd[c] = w * px2[c]; \
223 wgt[c] += w; \
224 sum[c] += pd[c]; \
225 } \
226 } while(0)
227
228#undef SUM_PIXEL_EPILOGUE
229#define SUM_PIXEL_EPILOGUE \
230 for_each_channel(c) \
231 { \
232 sum[c] /= wgt[c]; \
233 pcoarse[c] = sum[c]; \
234 const float det = (px[c] - sum[c]); \
235 pdetail[c] = det; \
236 sum_sq.v[c] += (det*det); \
237 } \
238 px += 4; \
239 pdetail += 4; \
240 pcoarse += 4;
241
242void eaw_dn_decompose(float *const restrict out, const float *const restrict in, float *const restrict detail,
243 dt_aligned_pixel_t sum_squared, const int scale, const float inv_sigma2,
244 const int32_t width, const int32_t height)
245{
246 const int mult = 1u << scale;
247 static const float filter[5] = { 1.0f / 16.0f, 4.0f / 16.0f, 6.0f / 16.0f, 4.0f / 16.0f, 1.0f / 16.0f };
248 const int boundary = 2 * mult;
249
250 _aligned_pixel sum_sq = { .v = { 0.0f } };
251
252#if !(defined(__apple_build_version__) && __apple_build_version__ < 11030000) //makes Xcode 11.3.1 compiler crash
253__OMP_PARALLEL_FOR__(reduction(vsum: sum_sq) )
254#endif
255 for(int rowid = 0; rowid < height; rowid++)
256 {
257 const size_t j = dwt_interleave_rows(rowid, height, mult);
258 const float *px = ((float *)in) + (size_t)4 * j * width;
259 const float *px2;
260 float *pdetail = detail + (size_t)4 * j * width;
261 float *pcoarse = out + (size_t)4 * j * width;
262
263 // for the first and last 'boundary' rows, we have to perform boundary tests for the entire row;
264 // for the central bulk, we only need to use those slower versions on the leftmost and rightmost pixels
265 const int lbound = (j < boundary || j >= height - boundary) ? width-boundary : boundary;
266
267 /* The first "2*mult" pixels need a boundary check because we might try to access past the left edge,
268 * which requires nearest pixel interpolation */
269 int i;
270 for(i = 0; i < lbound; i++)
271 {
273 for(int jj = 0; jj < 5; jj++)
274 {
275 const int y = j + mult * (jj-2);
276 const int clamp_y = CLAMP(y,0,height-1);
277 for(int ii = 0; ii < 5; ii++)
278 {
279 int x = i + mult * ((ii)-2);
280 if(x < 0) x = 0; // we might be looking past the left edge
281 px2 = ((float *)in) + 4 * x + (size_t)4 * clamp_y * width;
283 }
284 }
286 }
287
288 /* For pixels [2*mult, width-2*mult], we don't need to do any boundary checks */
289 for( ; i < width - boundary; i++)
290 {
292 px2 = ((float *)in) + (size_t)4 * (i - 2 * mult + (size_t)(j - 2 * mult) * width);
293 for(int jj = 0; jj < 5; jj++)
294 {
295 for(int ii = 0; ii < 5; ii++)
296 {
298 px2 += (size_t)4 * mult;
299 }
300 px2 += (size_t)4 * (width - 5) * mult;
301 }
303 }
304
305 /* Last 2*mult pixels in the row require the boundary check again */
306 for( ; i < width; i++)
307 {
309 for(int jj = 0; jj < 5; jj++)
310 {
311 const int y = j + mult * (jj-2);
312 const int clamp_y = CLAMP(y,0,height-1);
313 for(int ii = 0; ii < 5; ii++)
314 {
315 int x = i + mult * ((ii)-2);
316 if(x >= width) x = width - 1; // we might be looking past the right edge
317 px2 = ((float *)in) + 4 * x + (size_t)4 * clamp_y * width;
319 }
320 }
322 }
323 }
325 sum_squared[c] = sum_sq.v[c];
326}
327
328#undef SUM_PIXEL_CONTRIBUTION
329#undef SUM_PIXEL_PROLOGUE
330#undef SUM_PIXEL_EPILOGUE
331
332// clang-format off
333// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
334// vim: shiftwidth=2 expandtab tabstop=2 cindent
335// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
336// clang-format on
337
static const float x
const float threshold
const dt_colormatrix_t dt_aligned_pixel_t out
static int dwt_interleave_rows(const int rowid, const int height, const int stride)
Definition dwt.h:93
#define SUM_PIXEL_CONTRIBUTION(ii, jj)
Definition eaw.c:45
#define SUM_PIXEL_PROLOGUE
Definition eaw.c:62
void eaw_decompose(float *const restrict out, const float *const restrict in, float *const restrict detail, const int scale, const float sharpen, const int32_t width, const int32_t height)
Definition eaw.c:79
#define SUM_PIXEL_EPILOGUE
Definition eaw.c:66
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:29
struct _aligned_pixel _aligned_pixel
static float dn_weight(const float *c1, const float *c2, const float inv_sigma2)
Definition eaw.c:181
void eaw_dn_decompose(float *const restrict out, const float *const restrict in, float *const restrict detail, dt_aligned_pixel_t sum_squared, const int scale, const float inv_sigma2, const int32_t width, const int32_t height)
Definition eaw.c:242
void eaw_synthesize(float *const out, const float *const in, const float *const restrict detail, const float *const restrict threshold, const float *const restrict boost, const int32_t width, const int32_t height)
Definition eaw.c:157
float *const restrict const size_t k
static float fast_mexp2f(const float x)
Definition math.h:306
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define __OMP_SIMD__(...)
Definition openmp.h:99
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
#define for_four_channels(_var,...)
Definition simd.h:89
dt_aligned_pixel_t v
Definition eaw.c:199
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29