Skip to content

Commit 354a400

Browse files
committed
tone equalizer: better mask autotune
1 parent c0dc214 commit 354a400

File tree

1 file changed

+55
-48
lines changed

1 file changed

+55
-48
lines changed

src/iop/toneequal.c

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,67 +1349,67 @@ static inline void build_interpolation_matrix(float A[CHANNELS * PIXEL_CHAN],
13491349

13501350

13511351
__DT_CLONE_TARGETS__
1352-
static inline void compute_log_histogram(const float *const restrict luminance,
1352+
static inline void compute_log_histogram_and_stats(const float *const restrict luminance,
13531353
int histogram[UI_SAMPLES],
13541354
const size_t num_elem,
1355-
int *max_histogram)
1355+
int *max_histogram,
1356+
float *first_decile, float *last_decile)
13561357
{
13571358
// (Re)init the histogram
13581359
memset(histogram, 0, sizeof(int) * UI_SAMPLES);
13591360

1361+
// we first calculate an extended histogram for better accuracy
1362+
#define TEMP_SAMPLES 2 * UI_SAMPLES
1363+
int temp_hist[TEMP_SAMPLES];
1364+
memset(temp_hist, 0, sizeof(int) * TEMP_SAMPLES);
1365+
13601366
// Split exposure in bins
13611367
#ifdef _OPENMP
13621368
#pragma omp parallel for default(none) schedule(simd:static) \
13631369
dt_omp_firstprivate(luminance, num_elem) \
1364-
reduction(+:histogram[:UI_SAMPLES])
1370+
reduction(+:temp_hist[:TEMP_SAMPLES])
13651371
#endif
13661372
for(size_t k = 0; k < num_elem; k++)
13671373
{
1368-
// the histogram shows bins between [-14; +2] EV remapped between [0 ; UI_SAMPLES[
1369-
const int index = CLAMP((int)(((log2f(luminance[k]) + 8.0f) / 8.0f) * (float)UI_SAMPLES), 0, UI_SAMPLES - 1);
1370-
histogram[index] += 1;
1374+
// extended histogram bins between [-10; +6] EV remapped between [0 ; 2 * UI_SAMPLES]
1375+
const int index = CLAMP((int)(((log2f(luminance[k]) + 10.0f) / 16.0f) * (float)TEMP_SAMPLES), 0, TEMP_SAMPLES - 1);
1376+
temp_hist[index] += 1;
13711377
}
13721378

1373-
*max_histogram = 0;
1374-
1375-
for(int k = 0; k < UI_SAMPLES; k++)
1376-
{
1377-
// store the max numbers of elements in bins for later normalization
1378-
if(histogram[k] > *max_histogram)
1379-
*max_histogram = histogram[k];
1380-
}
1381-
}
1382-
1383-
1384-
static inline void histogram_deciles(const int histogram[UI_SAMPLES], size_t hist_bins, size_t num_elem,
1385-
const float hist_span, const float hist_offset,
1386-
float *first_decile, float *last_decile)
1387-
{
1388-
// Browse an histogram of `hist_bins` bins containing a population of `num_elems` elements
1389-
// spanning from `hist_offset` to `hist_offset + hist_span`,
1390-
// looking for the position of the first and last deciles,
1391-
// and return their values scaled in the corresponding span
1392-
1393-
const int first = (int)((float)num_elem * 0.1f);
1394-
const int last = (int)((float)num_elem * 0.9f);
1379+
const int first = (int)((float)num_elem * 0.05f);
1380+
const int last = (int)((float)num_elem * 0.95f);
13951381
int population = 0;
13961382
int first_pos = 0;
13971383
int last_pos = 0;
13981384

1399-
// scout the histogram bins looking for deciles
1400-
for(size_t k = 0; k < hist_bins; ++k)
1385+
// scout the extended histogram bins looking for deciles
1386+
// these would not be accurate with the regular histogram
1387+
for(size_t k = 0; k < TEMP_SAMPLES; ++k)
14011388
{
14021389
const size_t prev_population = population;
1403-
population += histogram[k];
1390+
population += temp_hist[k];
14041391
if(prev_population < first && first <= population) first_pos = k;
14051392
if(prev_population < last && last <= population) last_pos = k;
14061393
}
14071394

1408-
// Convert bins positions to exposures
1409-
*first_decile = (hist_span * (((float)first_pos) / ((float)(hist_bins - 1)))) + hist_offset;
1410-
*last_decile = (hist_span * (((float)last_pos) / ((float)(hist_bins - 1)))) + hist_offset;
1411-
}
1395+
// Convert decile positions to exposures
1396+
*first_decile = 16.0 * (float)first_pos / (float)(TEMP_SAMPLES - 1) - 10.0;
1397+
*last_decile = 16.0 * (float)last_pos / (float)(TEMP_SAMPLES - 1) - 10.0;
14121398

1399+
*max_histogram = 0;
1400+
// remap the extended histogram into the normal one
1401+
// bins between [-8; 0] EV remapped between [0 ; UI_SAMPLES]
1402+
for(size_t k = 0; k < TEMP_SAMPLES; ++k)
1403+
{
1404+
float EV = 16.0 * (float)k / (float)(TEMP_SAMPLES - 1) - 10.0;
1405+
int i = CLAMP((int)(((EV + 8.0f) / 8.0f) * (float)UI_SAMPLES), 0, UI_SAMPLES - 1);
1406+
histogram[i] += temp_hist[k];
1407+
1408+
// store the max numbers of elements in bins for later normalization
1409+
if(histogram[i] > *max_histogram)
1410+
*max_histogram = histogram[i];
1411+
}
1412+
}
14131413

14141414
static inline void update_histogram(struct dt_iop_module_t *const self)
14151415
{
@@ -1420,9 +1420,8 @@ static inline void update_histogram(struct dt_iop_module_t *const self)
14201420
if(!g->histogram_valid && g->luminance_valid)
14211421
{
14221422
const size_t num_elem = g->thumb_preview_buf_height * g->thumb_preview_buf_width;
1423-
compute_log_histogram(g->thumb_preview_buf, g->histogram, num_elem, &g->max_histogram);
1424-
histogram_deciles(g->histogram, UI_SAMPLES, num_elem, 8.0f, -8.0f,
1425-
&g->histogram_first_decile, &g->histogram_last_decile);
1423+
compute_log_histogram_and_stats(g->thumb_preview_buf, g->histogram, num_elem, &g->max_histogram,
1424+
&g->histogram_first_decile, &g->histogram_last_decile);
14261425
g->histogram_average = (g->histogram_first_decile + g->histogram_last_decile) / 2.0f;
14271426
g->histogram_valid = TRUE;
14281427
}
@@ -1761,14 +1760,20 @@ static void auto_adjust_exposure_boost(GtkWidget *quad, gpointer user_data)
17611760
// to spread it over as many nodes as possible for better exposure control.
17621761
// Controls nodes are between -8 and 0 EV,
17631762
// so we aim at centering the exposure distribution on -4 EV
1764-
const float target = log2f(CONTRAST_FULCRUM);
17651763

17661764
dt_iop_gui_enter_critical_section(self);
17671765
g->histogram_valid = 0;
17681766
dt_iop_gui_leave_critical_section(self);
17691767

17701768
update_histogram(self);
1771-
p->exposure_boost = target - g->histogram_average;
1769+
1770+
const float fd_old = exp2f(g->histogram_first_decile);
1771+
const float ld_old = exp2f(g->histogram_last_decile);
1772+
const float s1 = CONTRAST_FULCRUM - exp2f(-7.0);
1773+
const float s2 = exp2f(-1.0) - CONTRAST_FULCRUM;
1774+
const float mix = fd_old * s2 + ld_old * s1;
1775+
1776+
p->exposure_boost += log2f(CONTRAST_FULCRUM * (s1 + s2) / mix);
17721777

17731778
// Update the GUI stuff
17741779
++darktable.gui->reset;
@@ -1816,19 +1821,21 @@ static void auto_adjust_contrast_boost(GtkWidget *quad, gpointer user_data)
18161821
return;
18171822
}
18181823

1819-
// The goal is to spread 80 % of the exposure histogram between -4 ± 3 EV
1824+
// The goal is to spread 90 % of the exposure histogram in the [-7, -1] EV
18201825
dt_iop_gui_enter_critical_section(self);
18211826
g->histogram_valid = 0;
18221827
dt_iop_gui_leave_critical_section(self);
18231828

1824-
const float target = log2f(CONTRAST_FULCRUM);
18251829
update_histogram(self);
1826-
const float span_left = fabsf(target - g->histogram_first_decile);
1827-
const float span_right = fabsf(g->histogram_last_decile - target);
1828-
const float origin = fmaxf(span_left, span_right);
18291830

1830-
// Compute the correction
1831-
p->contrast_boost = (3.0f - origin);
1831+
// calculate the corrections
1832+
const float fd_old = exp2f(g->histogram_first_decile);
1833+
const float ld_old = exp2f(g->histogram_last_decile);
1834+
const float s1 = CONTRAST_FULCRUM - exp2f(-7.0);
1835+
const float s2 = exp2f(-1.0) - CONTRAST_FULCRUM;
1836+
const float mix = fd_old * s2 + ld_old * s1;
1837+
1838+
p->contrast_boost += log2f(mix / (CONTRAST_FULCRUM * (ld_old - fd_old)));
18321839

18331840
// Update the GUI stuff
18341841
++darktable.gui->reset;
@@ -3248,7 +3255,7 @@ void gui_init(struct dt_iop_module_t *self)
32483255
g_signal_connect(G_OBJECT(g->exposure_boost), "quad-pressed", G_CALLBACK(auto_adjust_exposure_boost), self);
32493256

32503257
g->contrast_boost = dt_bauhaus_slider_from_params(self, "contrast_boost");
3251-
dt_bauhaus_slider_set_soft_range(g->contrast_boost, -4.0, 4.0);
3258+
dt_bauhaus_slider_set_soft_range(g->contrast_boost, -2.0, 2.0);
32523259
dt_bauhaus_slider_set_format(g->contrast_boost, "%+.2f EV");
32533260
gtk_widget_set_tooltip_text(g->contrast_boost, _("use this to counter the averaging effect of the guided filter\n"
32543261
"and dilate the mask contrast around -4EV\n"

0 commit comments

Comments
 (0)