99 return _(
"chromatic aberrations");
106 _(
"linear or non-linear, Lab, display-referred"),
107 _(
"non-linear, Lab"),
108 _(
"non-linear, Lab, display-referred"));
124 return _(
"this module is deprecated. please use the chromatic aberration module instead.");
158static const float fib[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 };
161static inline void fib_latt(
int *
const x,
int *
const y,
float radius,
int step,
int idx)
164 if(idx >=
sizeof(
fib) /
sizeof(
float) - 1 || idx < 1)
168 fprintf(stderr,
"Fibonacci lattice index wrong/out of bounds in: defringe module\n");
171 float px = step /
fib[idx], py = step * (
fib[idx + 1] /
fib[idx]);
173 float dx = px * radius, dy = py * radius;
174 *
x = round(dx - radius / 2.0);
175 *y = round(dy - radius / 2.0);
178#define MAGIC_THRESHOLD_COEFF 33.0
217 const float sigma = fmax(0.1f, fabs(
d->radius)) * roi_in->
scale;
218 const float Labmax[] = { 100.0f, 128.0f, 128.0f, 1.0f };
219 const float Labmin[] = { 0.0f, -128.0f, -128.0f, 0.0f };
221 const int radius = ceil(2.0 * ceilf(
sigma));
225 int *xy_small = NULL;
227 if(roi_out->
width < 2 * radius + 1 || roi_out->
height < 2 * radius + 1)
goto ERROR_EXIT;
229 float avg_edge_chroma = 0.0;
231 const float *
const restrict in = (
float *
const)
i;
232 float *
const restrict
out = (
float *
const)o;
240 fprintf(stderr,
"Error allocating memory for gaussian blur in: defringe module\n");
247 const int samples_wish = radius * radius;
250 if(samples_wish > 89)
254 else if(samples_wish > 55)
258 else if(samples_wish > 34)
262 else if(samples_wish > 21)
266 else if(samples_wish > 13)
274 const int sampleidx_small = sampleidx_avg - 1;
276 const int small_radius =
MAX(radius, 3);
277 const int avg_radius = 24 + radius * 4;
279 const int samples_small =
fib[sampleidx_small];
280 const int samples_avg =
fib[sampleidx_avg];
284 xy_avg = malloc(
sizeof(
int) * 2 * samples_avg);
285 xy_small = malloc(
sizeof(
int) * 2 * samples_small);
288 fprintf(stderr,
"Error allocating memory for fibonacci lattice in: defringe module\n");
294 for(
int u = 0; u < samples_avg; u++)
297 fib_latt(&dx, &dy, avg_radius, u, sampleidx_avg);
301 for(
int u = 0; u < samples_small; u++)
304 fib_latt(&dx, &dy, small_radius, u, sampleidx_small);
306 xy_small[2*u+1] = dy;
311 for(
size_t j = 0; j < (size_t)
height *
width * 4; j += 4)
315 const float a = in[j + 1] -
out[j + 1];
316 const float b = in[j + 2] -
out[j + 2];
317 const float edge = (a * a + b * b);
322 avg_edge_chroma += edge * use_global_average;
326 if(use_global_average)
328 avg_edge_chroma = avg_edge_chroma / (
width *
height) + 10.0 * FLT_EPSILON;
335 thresh = fmax(0.1f,
d->thresh);
341#pragma omp parallel for default(firstprivate) \
346 const size_t row_above = (size_t)
MAX(0, (
v-1)) *
width *
ch;
347 const size_t curr_row = (size_t)
v *
width *
ch;
351 const size_t index =
ch * ((size_t)
v *
width +
t);
352 float local_thresh = thresh;
356 float local_avg = 0.0;
358 for(
int u = 0; u < samples_avg; u++)
360 const int dx = xy_avg[2*u];
361 const int dy = xy_avg[2*u+1];
362 const int x = CLAMP(
t + dx, 0,
width - 1);
363 const int y = CLAMP(
v + dy, 0,
height - 1);
364 local_avg +=
out[((size_t)y *
width +
x) *
ch + 3];
366 avg_edge_chroma = fmax(0.01f, (
float)local_avg / samples_avg);
370 if(
out[index + 3] > local_thresh
372 ||
out[row_above +
MAX(0, (
t - 1)) *
ch + 3] > local_thresh
373 ||
out[row_above +
t *
ch + 3] > local_thresh
374 ||
out[row_above +
MIN(
width - 1, (
t + 1)) *
ch + 3] > local_thresh
375 ||
out[curr_row +
MAX(0, (
t - 1)) *
ch + 3] > local_thresh
376 ||
out[curr_row +
MIN(
width - 1, (
t + 1)) *
ch + 3] > local_thresh
377 ||
out[row_below +
MAX(0, (
t - 1)) *
ch + 3] > local_thresh
378 ||
out[row_below +
t *
ch + 3] > local_thresh
379 ||
out[row_below +
MIN(
width - 1, (
t + 1)) *
ch + 3] > local_thresh)
381 float atot = 0, btot = 0;
391 for(
int u = 0; u < samples_small; u++)
393 const int dx = xy_small[2*u];
394 const int dy = xy_small[2*u+1];
395 const int x = CLAMP(
t + dx, 0,
width - 1);
396 const int y = CLAMP(
v + dy, 0,
height - 1);
397 const size_t idx =
ch * ((size_t)y *
width +
x);
400 weight = 1.0 / (
out[idx + 3] + avg_edge_chroma);
401 atot +=
weight * in[idx + 1];
402 btot +=
weight * in[idx + 2];
410 double a = (atot / norm);
411 double b = (btot / norm);
412 out[index] = in[index];
420 for(
int c = 0; c < 3; c++)
422 out[index+c] = in[index+c];
429 dt_iop_alpha_copy(
i, o, roi_out->
width, roi_out->
height);
447 gtk_widget_set_tooltip_text(
g->mode_select,
448 _(
"method for color protection:\n - global average: fast, might show slightly wrong previews in high "
449 "magnification; might sometimes protect saturation too much or too low in comparison to local "
450 "average\n - local average: slower, might protect saturation better than global average by using "
451 "near pixels as color reference, so it can still allow for more desaturation where required\n - "
452 "static: fast, only uses the threshold as a static limit"));
455 gtk_widget_set_tooltip_text(
g->radius_scale, _(
"radius for detecting fringe"));
458 gtk_widget_set_tooltip_text(
g->thresh_scale, _(
"threshold for defringe, higher values mean less defringing"));
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
const char ** description(struct dt_iop_module_t *self)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *module, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
static void fib_latt(int *const x, int *const y, float radius, int step, int idx)
void gui_update(dt_iop_module_t *module)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
dt_iop_defringe_params_t dt_iop_defringe_data_t
void gui_init(dt_iop_module_t *self)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
const char * deprecated_msg()
#define MAGIC_THRESHOLD_COEFF
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
void dt_gaussian_free(dt_gaussian_t *g)
void dt_gaussian_blur_4c(dt_gaussian_t *g, const float *const in, float *const out)
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)
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
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)
@ IOP_FLAGS_SUPPORTS_BLENDING
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
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...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_SIMD__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
struct dt_iop_module_t *void * data
dt_iop_defringe_mode_t op_mode
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__