170 return _(
"chromatic a_berrations");
177 _(
"linear, raw, scene-referred"),
179 _(
"linear, raw, scene-referred"));
208 const float weighth = fmaxf(blurred_manifold_higher[
k * 4 + 3], 1E-2f);
209 const float weightl = fmaxf(blurred_manifold_lower[
k * 4 + 3], 1E-2f);
212 const float highg = blurred_manifold_higher[
k * 4 + guide] / weighth;
213 const float lowg = blurred_manifold_lower[
k * 4 + guide] / weightl;
215 blurred_manifold_higher[
k * 4 + guide] = highg;
216 blurred_manifold_lower[
k * 4 + guide] = lowg;
219 for(
size_t kc = 0; kc <= 1; kc++)
221 const size_t c = (kc + guide + 1) % 3;
222 const float highc = blurred_manifold_higher[
k * 4 + c] / weighth;
223 const float lowc = blurred_manifold_lower[
k * 4 + c] / weightl;
224 blurred_manifold_higher[
k * 4 + c] = exp2f(highc) * highg;
225 blurred_manifold_lower[
k * 4 + c] = exp2f(lowc) * lowg;
233 const float w = (weighth - 0.01f) / (0.05f - 0.01f);
236 blurred_manifold_higher[
k * 4 + c] = w * blurred_manifold_higher[
k * 4 + c]
237 + (1.0f - w) * blurred_in[
k * 4 + c];
244 const float w = (weightl - 0.01f) / (0.05f - 0.01f);
247 blurred_manifold_lower[
k * 4 + c] = w * blurred_manifold_lower[
k * 4 + c]
248 + (1.0f - w) * blurred_in[
k * 4 + c];
254#define DT_CACORRECTRGB_MAX_EV_DIFF 2.0f
257 const float sigma,
const float sigma2,
259 float*
const restrict manifolds, gboolean refine_manifolds)
279 const float blur_size = refine_manifolds ? sigma2 :
sigma;
295 const float pixelg = fmaxf(in[
k * 4 + guide], 1E-6f);
296 const float avg = blurred_in[
k * 4 + guide];
297 float weighth = (pixelg >= avg);
298 float weightl = (pixelg <= avg);
300 for(
size_t kc = 0; kc <= 1; kc++)
302 const size_t c = (kc + guide + 1) % 3;
303 const float pixel = fmaxf(in[
k * 4 + c], 1E-6f);
304 const float log_diff = log2f(pixel / pixelg);
305 logdiffs[kc] = log_diff;
309 const float maxlogdiff = fmaxf(fabsf(logdiffs[0]), fabsf(logdiffs[1]));
313 weightl *= correction_weight;
314 weighth *= correction_weight;
316 for(
size_t kc = 0; kc <= 1; kc++)
318 const size_t c = (kc + guide + 1) % 3;
319 manifold_higher[
k * 4 + c] = logdiffs[kc] * weighth;
320 manifold_lower[
k * 4 + c] = logdiffs[kc] * weightl;
322 manifold_higher[
k * 4 + guide] = pixelg * weighth;
323 manifold_lower[
k * 4 + guide] = pixelg * weightl;
324 manifold_higher[
k * 4 + 3] = weighth;
325 manifold_lower[
k * 4 + 3] = weightl;
386 const float pixelg = log2f(fmaxf(in[
k * 4 + guide], 1E-6f));
387 const float highg = log2f(fmaxf(blurred_manifold_higher[
k * 4 + guide], 1E-6f));
388 const float lowg = log2f(fmaxf(blurred_manifold_lower[
k * 4 + guide], 1E-6f));
389 const float avgg = log2f(fmaxf(blurred_in[
k * 4 + guide], 1E-6f));
392 for(
size_t kc = 0; kc <= 1; kc++)
394 const size_t c = (guide + kc + 1) % 3;
399 const float pixel = log2f(fmaxf(in[
k * 4 + c], 1E-6f));
400 const float highc = log2f(fmaxf(blurred_manifold_higher[
k * 4 + c], 1E-6f));
401 const float lowc = log2f(fmaxf(blurred_manifold_lower[
k * 4 + c], 1E-6f));
406 const float dist_to_ll = fabsf(pixelg - lowg - pixel + lowc);
407 const float dist_to_hh = fabsf(pixelg - highg - pixel + highc);
408 const float dist_to_lh = fabsf((pixelg - pixel) - (highg - lowc));
409 const float dist_to_hl = fabsf((pixelg - pixel) - (lowg - highc));
411 float dist_to_good = 1.0f;
412 if(fabsf(pixelg - lowg) < fabsf(pixelg - highg))
413 dist_to_good = dist_to_ll;
415 dist_to_good = dist_to_hh;
417 float dist_to_bad = 1.0f;
418 if(fabsf(pixelg - lowg) < fabsf(pixelg - highg))
419 dist_to_bad = dist_to_hl;
421 dist_to_bad = dist_to_lh;
424 w *= 1.0f * (0.2f + 1.0f / fmaxf(dist_to_good, 0.1f)) / (0.2f + 1.0f / fmaxf(dist_to_bad, 0.1f));
430 for(
size_t kc = 0; kc <= 1; kc++)
432 const size_t c = (guide + kc + 1) % 3;
433 const float pixel = fmaxf(in[
k * 4 + c], 1E-6f);
434 const float log_diff = log2f(pixel) - pixelg;
435 logdiffs[kc] = log_diff;
439 const float maxlogdiff = fmaxf(fabsf(logdiffs[0]), fabsf(logdiffs[1]));
443 w *= correction_weight;
445 for(
size_t kc = 0; kc <= 1; kc++)
447 const size_t c = (kc + guide + 1) % 3;
448 manifold_higher[
k * 4 + c] = logdiffs[kc] * w;
450 manifold_higher[
k * 4 + guide] = fmaxf(in[
k * 4 + guide], 0.0f) * w;
451 manifold_higher[
k * 4 + 3] = w;
456 manifold_lower[
k * 4 + c] = 0.0f;
462 for(
size_t kc = 0; kc <= 1; kc++)
464 const size_t c = (guide + kc + 1) % 3;
465 const float pixel = fmaxf(in[
k * 4 + c], 1E-6f);
466 const float log_diff = log2f(pixel) - pixelg;
467 logdiffs[kc] = log_diff;
471 const float maxlogdiff = fmaxf(fabsf(logdiffs[0]), fabsf(logdiffs[1]));
475 w *= correction_weight;
477 for(
size_t kc = 0; kc <= 1; kc++)
479 const size_t c = (kc + guide + 1) % 3;
480 manifold_lower[
k * 4 + c] = logdiffs[kc] * w;
482 manifold_lower[
k * 4 + guide] = fmaxf(in[
k * 4 + guide], 0.0f) * w;
483 manifold_lower[
k * 4 + 3] = w;
486 for(
size_t c = 0; c < 4; c++)
488 manifold_higher[
k * 4 + c] = 0.0f;
503 for(
size_t c = 0; c < 3; c++)
505 manifolds[
k * 6 + c] = blurred_manifold_higher[
k * 4 + c];
506 manifolds[
k * 6 + 3 + c] = blurred_manifold_lower[
k * 4 + c];
518#undef DT_CACORRECTRGB_MAX_EV_DIFF
522 const float*
const restrict manifolds,
526 float*
const restrict
out)
532 const float high_guide = fmaxf(manifolds[
k * 6 + guide], 1E-6f);
533 const float low_guide = fmaxf(manifolds[
k * 6 + 3 + guide], 1E-6f);
534 const float log_high = log2f(high_guide);
535 const float log_low = log2f(low_guide);
536 const float dist_low_high = log_high - log_low;
537 const float pixelg = fmaxf(in[
k * 4 + guide], 0.0f);
538 const float log_pixg = log2f(fminf(fmaxf(pixelg, low_guide), high_guide));
544 float weight_low = fabsf(log_high - log_pixg) / fmaxf(dist_low_high, 1E-6f);
549 const float threshold_dist_low_high = 0.25f;
550 if(dist_low_high < threshold_dist_low_high)
552 const float weight = dist_low_high / threshold_dist_low_high;
555 weight_low = weight_low *
weight + 0.5f * (1.0f -
weight);
557 const float weight_high = fmaxf(1.0f - weight_low, 0.0f);
559 for(
size_t kc = 0; kc <= 1; kc++)
561 const size_t c = (guide + kc + 1) % 3;
562 const float pixelc = fmaxf(in[
k * 4 + c], 0.0f);
564 const float ratio_high_manifolds = manifolds[
k * 6 + c] / high_guide;
565 const float ratio_low_manifolds = manifolds[
k * 6 + 3 + c] / low_guide;
567 const float ratio = powf(ratio_low_manifolds, weight_low) * powf(ratio_high_manifolds, weight_high);
569 const float outp = pixelg * ratio;
574 out[
k * 4 + c] = outp;
577 out[
k * 4 + c] = fminf(outp, pixelc);
580 out[
k * 4 + c] = fmaxf(outp, pixelc);
585 out[
k * 4 + guide] = pixelg;
586 out[
k * 4 + 3] = in[
k * 4 + 3];
595 float*
const restrict
out)
613 for(
size_t kc = 0; kc <= 1; kc++)
615 const size_t c = (guide + kc + 1) % 3;
616 in_out[
k * 4 + kc * 2 + 0] = in[
k * 4 + c];
617 in_out[
k * 4 + kc * 2 + 1] =
out[
k * 4 + c];
645 for(
size_t kc = 0; kc <= 1; kc++)
647 const float avg_in = log2f(fmaxf(blurred_in_out[
k * 4 + kc * 2 + 0], 1E-6f));
648 const float avg_out = log2f(fmaxf(blurred_in_out[
k * 4 + kc * 2 + 1], 1E-6f));
649 w *= expf(-fmaxf(fabsf(avg_out - avg_in), 0.01f) * safety);
651 for(
size_t kc = 0; kc <= 1; kc++)
653 const size_t c = (guide + kc + 1) % 3;
654 out[
k * 4 + c] = fmaxf(1.0f - w, 0.0f) * fmaxf(in[
k * 4 + c], 0.0f) + w * fmaxf(
out[
k * 4 + c], 0.0f);
665static inline __attribute__((always_inline))
int reduce_chromatic_aberrations(
const float*
const restrict in,
667 const size_t ch,
const float sigma,
const float sigma2,
670 const gboolean refine_manifolds,
672 float*
const restrict
out)
676 const float downsize = fminf(3.0f,
sigma);
677 const size_t ds_width =
width / downsize;
678 const size_t ds_height =
height / downsize;
695 if(
get_manifolds(ds_in, ds_width, ds_height,
sigma / downsize, sigma2 / downsize, guide, ds_manifolds, refine_manifolds))
729 const float* in = (
float*)ivoid;
731 const float sigma = fmaxf(
d->radius / scale, 1.0f);
732 const float sigma2 = fmaxf(
d->radius *
d->radius / scale, 1.0f);
736 const float safety = powf(20.0f, 1.0f -
d->strength);
737 return reduce_chromatic_aberrations(in,
width,
height,
ch,
sigma, sigma2,
d->guide_channel,
d->mode,
d->refine_manifolds, safety,
out);
745 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->refine_manifolds),
p->refine_manifolds);
756 d->refine_manifolds =
FALSE;
766 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->refine_manifolds),
d->refine_manifolds);
775 gtk_widget_set_tooltip_text(
g->guide_channel, _(
"channel used as a reference to\n"
776 "correct the other channels.\n"
777 "use sharpest channel if some\n"
778 "channels are blurry.\n"
779 "try changing guide channel if you\n"
782 gtk_widget_set_tooltip_text(
g->radius, _(
"increase for stronger correction"));
784 gtk_widget_set_tooltip_text(
g->strength, _(
"balance between smoothing colors\n"
785 "and preserving them.\n"
786 "high values can lead to overshooting\n"
787 "and edge bleeding."));
791 gtk_widget_set_tooltip_text(
g->mode, _(
"correction mode to use.\n"
792 "can help with multiple\n"
793 "instances for very damaged\n"
795 "darken only is particularly\n"
796 "efficient to correct blue\n"
797 "chromatic aberration."));
799 gtk_widget_set_tooltip_text(
g->refine_manifolds, _(
"runs an iterative approach\n"
800 "with several radii.\n"
801 "improves result on images\n"
802 "with very large chromatic\n"
803 "aberrations, but can smooth\n"
804 "colors too much on other\n"
static void error(char *msg)
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
void dt_bauhaus_combobox_set_default(GtkWidget *widget, int def)
const char ** description(struct dt_iop_module_t *self)
void reload_defaults(dt_iop_module_t *module)
dt_iop_cacorrectrgb_mode_t
@ DT_CACORRECT_MODE_DARKEN
@ DT_CACORRECT_MODE_STANDARD
@ DT_CACORRECT_MODE_BRIGHTEN
dt_iop_cacorrectrgb_guide_channel_t
void gui_update(dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void apply_correction(const float *const restrict in, const float *const restrict manifolds, const size_t width, const size_t height, const float sigma, const dt_iop_cacorrectrgb_guide_channel_t guide, const dt_iop_cacorrectrgb_mode_t mode, float *const restrict out)
#define DT_CACORRECTRGB_MAX_EV_DIFF
static __DT_CLONE_TARGETS__ void normalize_manifolds(const float *const restrict blurred_in, float *const restrict blurred_manifold_lower, float *const restrict blurred_manifold_higher, const size_t width, const size_t height, const dt_iop_cacorrectrgb_guide_channel_t guide)
void gui_init(dt_iop_module_t *self)
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static __DT_CLONE_TARGETS__ int get_manifolds(const float *const restrict in, const size_t width, const size_t height, const float sigma, const float sigma2, const dt_iop_cacorrectrgb_guide_channel_t guide, float *const restrict manifolds, gboolean refine_manifolds)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
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)
static __DT_CLONE_TARGETS__ int reduce_artifacts(const float *const restrict in, const size_t width, const size_t height, const float sigma, const dt_iop_cacorrectrgb_guide_channel_t guide, const float safety, float *const restrict out)
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
#define for_each_channel(_var,...)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define dt_pixelpipe_cache_free_align(mem)
#define __DT_CLONE_TARGETS__
#define for_four_channels(_var,...)
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
static __DT_CLONE_TARGETS__ void interpolate_bilinear(const float *const restrict in, const size_t width_in, const size_t height_in, float *const restrict out, const size_t width_out, const size_t height_out, const size_t ch)
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 GtkWidget * dt_ui_section_label_new(const gchar *str)
#define DT_GUI_BOX_SPACING
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)
float dt_dev_get_module_scale(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi_in)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
#define IOP_GUI_ALLOC(module)
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
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)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
struct _GtkWidget GtkWidget
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
GtkWidget * guide_channel
GtkWidget * refine_manifolds
dt_iop_cacorrectrgb_mode_t mode
gboolean refine_manifolds
dt_iop_cacorrectrgb_guide_channel_t guide_channel
dt_iop_params_t * default_params
dt_iop_gui_data_t * gui_data
Region of interest passed through the pixelpipe.