172 return _(
"chromatic a_berrations");
179 _(
"linear, raw, scene-referred"),
181 _(
"linear, raw, scene-referred"));
210 const float weighth = fmaxf(blurred_manifold_higher[
k * 4 + 3], 1E-2f);
211 const float weightl = fmaxf(blurred_manifold_lower[
k * 4 + 3], 1E-2f);
214 const float highg = blurred_manifold_higher[
k * 4 + guide] / weighth;
215 const float lowg = blurred_manifold_lower[
k * 4 + guide] / weightl;
217 blurred_manifold_higher[
k * 4 + guide] = highg;
218 blurred_manifold_lower[
k * 4 + guide] = lowg;
221 for(
size_t kc = 0; kc <= 1; kc++)
223 const size_t c = (kc + guide + 1) % 3;
224 const float highc = blurred_manifold_higher[
k * 4 + c] / weighth;
225 const float lowc = blurred_manifold_lower[
k * 4 + c] / weightl;
226 blurred_manifold_higher[
k * 4 + c] = exp2f(highc) * highg;
227 blurred_manifold_lower[
k * 4 + c] = exp2f(lowc) * lowg;
235 const float w = (weighth - 0.01f) / (0.05f - 0.01f);
238 blurred_manifold_higher[
k * 4 + c] = w * blurred_manifold_higher[
k * 4 + c]
239 + (1.0f - w) * blurred_in[
k * 4 + c];
246 const float w = (weightl - 0.01f) / (0.05f - 0.01f);
249 blurred_manifold_lower[
k * 4 + c] = w * blurred_manifold_lower[
k * 4 + c]
250 + (1.0f - w) * blurred_in[
k * 4 + c];
256#define DT_CACORRECTRGB_MAX_EV_DIFF 2.0f
259 const float sigma,
const float sigma2,
261 float*
const restrict manifolds, gboolean refine_manifolds)
281 const float blur_size = refine_manifolds ? sigma2 :
sigma;
297 const float pixelg = fmaxf(in[
k * 4 + guide], 1E-6f);
298 const float avg = blurred_in[
k * 4 + guide];
299 float weighth = (pixelg >= avg);
300 float weightl = (pixelg <= avg);
302 for(
size_t kc = 0; kc <= 1; kc++)
304 const size_t c = (kc + guide + 1) % 3;
305 const float pixel = fmaxf(in[
k * 4 + c], 1E-6f);
306 const float log_diff = log2f(pixel / pixelg);
307 logdiffs[kc] = log_diff;
311 const float maxlogdiff = fmaxf(fabsf(logdiffs[0]), fabsf(logdiffs[1]));
315 weightl *= correction_weight;
316 weighth *= correction_weight;
318 for(
size_t kc = 0; kc <= 1; kc++)
320 const size_t c = (kc + guide + 1) % 3;
321 manifold_higher[
k * 4 + c] = logdiffs[kc] * weighth;
322 manifold_lower[
k * 4 + c] = logdiffs[kc] * weightl;
324 manifold_higher[
k * 4 + guide] = pixelg * weighth;
325 manifold_lower[
k * 4 + guide] = pixelg * weightl;
326 manifold_higher[
k * 4 + 3] = weighth;
327 manifold_lower[
k * 4 + 3] = weightl;
388 const float pixelg = log2f(fmaxf(in[
k * 4 + guide], 1E-6f));
389 const float highg = log2f(fmaxf(blurred_manifold_higher[
k * 4 + guide], 1E-6f));
390 const float lowg = log2f(fmaxf(blurred_manifold_lower[
k * 4 + guide], 1E-6f));
391 const float avgg = log2f(fmaxf(blurred_in[
k * 4 + guide], 1E-6f));
394 for(
size_t kc = 0; kc <= 1; kc++)
396 const size_t c = (guide + kc + 1) % 3;
401 const float pixel = log2f(fmaxf(in[
k * 4 + c], 1E-6f));
402 const float highc = log2f(fmaxf(blurred_manifold_higher[
k * 4 + c], 1E-6f));
403 const float lowc = log2f(fmaxf(blurred_manifold_lower[
k * 4 + c], 1E-6f));
408 const float dist_to_ll = fabsf(pixelg - lowg - pixel + lowc);
409 const float dist_to_hh = fabsf(pixelg - highg - pixel + highc);
410 const float dist_to_lh = fabsf((pixelg - pixel) - (highg - lowc));
411 const float dist_to_hl = fabsf((pixelg - pixel) - (lowg - highc));
413 float dist_to_good = 1.0f;
414 if(fabsf(pixelg - lowg) < fabsf(pixelg - highg))
415 dist_to_good = dist_to_ll;
417 dist_to_good = dist_to_hh;
419 float dist_to_bad = 1.0f;
420 if(fabsf(pixelg - lowg) < fabsf(pixelg - highg))
421 dist_to_bad = dist_to_hl;
423 dist_to_bad = dist_to_lh;
426 w *= 1.0f * (0.2f + 1.0f / fmaxf(dist_to_good, 0.1f)) / (0.2f + 1.0f / fmaxf(dist_to_bad, 0.1f));
432 for(
size_t kc = 0; kc <= 1; kc++)
434 const size_t c = (guide + kc + 1) % 3;
435 const float pixel = fmaxf(in[
k * 4 + c], 1E-6f);
436 const float log_diff = log2f(pixel) - pixelg;
437 logdiffs[kc] = log_diff;
441 const float maxlogdiff = fmaxf(fabsf(logdiffs[0]), fabsf(logdiffs[1]));
445 w *= correction_weight;
447 for(
size_t kc = 0; kc <= 1; kc++)
449 const size_t c = (kc + guide + 1) % 3;
450 manifold_higher[
k * 4 + c] = logdiffs[kc] * w;
452 manifold_higher[
k * 4 + guide] = fmaxf(in[
k * 4 + guide], 0.0f) * w;
453 manifold_higher[
k * 4 + 3] = w;
458 manifold_lower[
k * 4 + c] = 0.0f;
464 for(
size_t kc = 0; kc <= 1; kc++)
466 const size_t c = (guide + kc + 1) % 3;
467 const float pixel = fmaxf(in[
k * 4 + c], 1E-6f);
468 const float log_diff = log2f(pixel) - pixelg;
469 logdiffs[kc] = log_diff;
473 const float maxlogdiff = fmaxf(fabsf(logdiffs[0]), fabsf(logdiffs[1]));
477 w *= correction_weight;
479 for(
size_t kc = 0; kc <= 1; kc++)
481 const size_t c = (kc + guide + 1) % 3;
482 manifold_lower[
k * 4 + c] = logdiffs[kc] * w;
484 manifold_lower[
k * 4 + guide] = fmaxf(in[
k * 4 + guide], 0.0f) * w;
485 manifold_lower[
k * 4 + 3] = w;
488 for(
size_t c = 0; c < 4; c++)
490 manifold_higher[
k * 4 + c] = 0.0f;
505 for(
size_t c = 0; c < 3; c++)
507 manifolds[
k * 6 + c] = blurred_manifold_higher[
k * 4 + c];
508 manifolds[
k * 6 + 3 + c] = blurred_manifold_lower[
k * 4 + c];
520#undef DT_CACORRECTRGB_MAX_EV_DIFF
524 const float*
const restrict manifolds,
528 float*
const restrict
out)
534 const float high_guide = fmaxf(manifolds[
k * 6 + guide], 1E-6f);
535 const float low_guide = fmaxf(manifolds[
k * 6 + 3 + guide], 1E-6f);
536 const float log_high = log2f(high_guide);
537 const float log_low = log2f(low_guide);
538 const float dist_low_high = log_high - log_low;
539 const float pixelg = fmaxf(in[
k * 4 + guide], 0.0f);
540 const float log_pixg = log2f(fminf(fmaxf(pixelg, low_guide), high_guide));
546 float weight_low = fabsf(log_high - log_pixg) / fmaxf(dist_low_high, 1E-6f);
551 const float threshold_dist_low_high = 0.25f;
552 if(dist_low_high < threshold_dist_low_high)
554 const float weight = dist_low_high / threshold_dist_low_high;
557 weight_low = weight_low *
weight + 0.5f * (1.0f -
weight);
559 const float weight_high = fmaxf(1.0f - weight_low, 0.0f);
561 for(
size_t kc = 0; kc <= 1; kc++)
563 const size_t c = (guide + kc + 1) % 3;
564 const float pixelc = fmaxf(in[
k * 4 + c], 0.0f);
566 const float ratio_high_manifolds = manifolds[
k * 6 + c] / high_guide;
567 const float ratio_low_manifolds = manifolds[
k * 6 + 3 + c] / low_guide;
569 const float ratio = powf(ratio_low_manifolds, weight_low) * powf(ratio_high_manifolds, weight_high);
571 const float outp = pixelg * ratio;
576 out[
k * 4 + c] = outp;
579 out[
k * 4 + c] = fminf(outp, pixelc);
582 out[
k * 4 + c] = fmaxf(outp, pixelc);
587 out[
k * 4 + guide] = pixelg;
588 out[
k * 4 + 3] = in[
k * 4 + 3];
597 float*
const restrict
out)
615 for(
size_t kc = 0; kc <= 1; kc++)
617 const size_t c = (guide + kc + 1) % 3;
618 in_out[
k * 4 + kc * 2 + 0] = in[
k * 4 + c];
619 in_out[
k * 4 + kc * 2 + 1] =
out[
k * 4 + c];
647 for(
size_t kc = 0; kc <= 1; kc++)
649 const float avg_in = log2f(fmaxf(blurred_in_out[
k * 4 + kc * 2 + 0], 1E-6f));
650 const float avg_out = log2f(fmaxf(blurred_in_out[
k * 4 + kc * 2 + 1], 1E-6f));
651 w *= expf(-fmaxf(fabsf(avg_out - avg_in), 0.01f) * safety);
653 for(
size_t kc = 0; kc <= 1; kc++)
655 const size_t c = (guide + kc + 1) % 3;
656 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);
667static inline __attribute__((always_inline))
int reduce_chromatic_aberrations(
const float*
const restrict in,
669 const size_t ch,
const float sigma,
const float sigma2,
672 const gboolean refine_manifolds,
674 float*
const restrict
out)
678 const float downsize = fminf(3.0f,
sigma);
679 const size_t ds_width =
width / downsize;
680 const size_t ds_height =
height / downsize;
697 if(
get_manifolds(ds_in, ds_width, ds_height,
sigma / downsize, sigma2 / downsize, guide, ds_manifolds, refine_manifolds))
731 const float* in = (
float*)ivoid;
733 const float sigma = fmaxf(
d->radius / scale, 1.0f);
734 const float sigma2 = fmaxf(
d->radius *
d->radius / scale, 1.0f);
738 const float safety = powf(20.0f, 1.0f -
d->strength);
739 return reduce_chromatic_aberrations(in,
width,
height,
ch,
sigma, sigma2,
d->guide_channel,
d->mode,
d->refine_manifolds, safety,
out);
756 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->refine_manifolds),
p->refine_manifolds);
767 d->refine_manifolds =
FALSE;
777 gtk_widget_set_tooltip_text(
g->guide_channel, _(
"channel used as a reference to\n"
778 "correct the other channels.\n"
779 "use sharpest channel if some\n"
780 "channels are blurry.\n"
781 "try changing guide channel if you\n"
784 gtk_widget_set_tooltip_text(
g->radius, _(
"increase for stronger correction"));
786 gtk_widget_set_tooltip_text(
g->strength, _(
"balance between smoothing colors\n"
787 "and preserving them.\n"
788 "high values can lead to overshooting\n"
789 "and edge bleeding."));
793 gtk_widget_set_tooltip_text(
g->mode, _(
"correction mode to use.\n"
794 "can help with multiple\n"
795 "instances for very damaged\n"
797 "darken only is particularly\n"
798 "efficient to correct blue\n"
799 "chromatic aberration."));
801 gtk_widget_set_tooltip_text(
g->refine_manifolds, _(
"runs an iterative approach\n"
802 "with several radii.\n"
803 "improves result on images\n"
804 "with very large chromatic\n"
805 "aberrations, but can smooth\n"
806 "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)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
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)
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
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)
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)
GtkWidget * dt_ui_section_label_new(const gchar *str)
float *const restrict const size_t k
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_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
#define for_each_channel(_var,...)
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...
#define for_four_channels(_var,...)
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
struct dt_iop_module_gui_t * gui
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__