@@ -1349,67 +1349,67 @@ static inline void build_interpolation_matrix(float A[CHANNELS * PIXEL_CHAN],
1349
1349
1350
1350
1351
1351
__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 ,
1353
1353
int histogram [UI_SAMPLES ],
1354
1354
const size_t num_elem ,
1355
- int * max_histogram )
1355
+ int * max_histogram ,
1356
+ float * first_decile , float * last_decile )
1356
1357
{
1357
1358
// (Re)init the histogram
1358
1359
memset (histogram , 0 , sizeof (int ) * UI_SAMPLES );
1359
1360
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
+
1360
1366
// Split exposure in bins
1361
1367
#ifdef _OPENMP
1362
1368
#pragma omp parallel for default(none) schedule(simd:static) \
1363
1369
dt_omp_firstprivate(luminance, num_elem) \
1364
- reduction(+:histogram[:UI_SAMPLES ])
1370
+ reduction(+:temp_hist[:TEMP_SAMPLES ])
1365
1371
#endif
1366
1372
for (size_t k = 0 ; k < num_elem ; k ++ )
1367
1373
{
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 ;
1371
1377
}
1372
1378
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 );
1395
1381
int population = 0 ;
1396
1382
int first_pos = 0 ;
1397
1383
int last_pos = 0 ;
1398
1384
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 )
1401
1388
{
1402
1389
const size_t prev_population = population ;
1403
- population += histogram [k ];
1390
+ population += temp_hist [k ];
1404
1391
if (prev_population < first && first <= population ) first_pos = k ;
1405
1392
if (prev_population < last && last <= population ) last_pos = k ;
1406
1393
}
1407
1394
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 ;
1412
1398
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
+ }
1413
1413
1414
1414
static inline void update_histogram (struct dt_iop_module_t * const self )
1415
1415
{
@@ -1420,9 +1420,8 @@ static inline void update_histogram(struct dt_iop_module_t *const self)
1420
1420
if (!g -> histogram_valid && g -> luminance_valid )
1421
1421
{
1422
1422
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 );
1426
1425
g -> histogram_average = (g -> histogram_first_decile + g -> histogram_last_decile ) / 2.0f ;
1427
1426
g -> histogram_valid = TRUE;
1428
1427
}
@@ -1761,14 +1760,20 @@ static void auto_adjust_exposure_boost(GtkWidget *quad, gpointer user_data)
1761
1760
// to spread it over as many nodes as possible for better exposure control.
1762
1761
// Controls nodes are between -8 and 0 EV,
1763
1762
// so we aim at centering the exposure distribution on -4 EV
1764
- const float target = log2f (CONTRAST_FULCRUM );
1765
1763
1766
1764
dt_iop_gui_enter_critical_section (self );
1767
1765
g -> histogram_valid = 0 ;
1768
1766
dt_iop_gui_leave_critical_section (self );
1769
1767
1770
1768
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 );
1772
1777
1773
1778
// Update the GUI stuff
1774
1779
++ darktable .gui -> reset ;
@@ -1816,19 +1821,21 @@ static void auto_adjust_contrast_boost(GtkWidget *quad, gpointer user_data)
1816
1821
return ;
1817
1822
}
1818
1823
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
1820
1825
dt_iop_gui_enter_critical_section (self );
1821
1826
g -> histogram_valid = 0 ;
1822
1827
dt_iop_gui_leave_critical_section (self );
1823
1828
1824
- const float target = log2f (CONTRAST_FULCRUM );
1825
1829
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 );
1829
1830
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 )));
1832
1839
1833
1840
// Update the GUI stuff
1834
1841
++ darktable .gui -> reset ;
@@ -3248,7 +3255,7 @@ void gui_init(struct dt_iop_module_t *self)
3248
3255
g_signal_connect (G_OBJECT (g -> exposure_boost ), "quad-pressed" , G_CALLBACK (auto_adjust_exposure_boost ), self );
3249
3256
3250
3257
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 );
3252
3259
dt_bauhaus_slider_set_format (g -> contrast_boost , "%+.2f EV" );
3253
3260
gtk_widget_set_tooltip_text (g -> contrast_boost , _ ("use this to counter the averaging effect of the guided filter\n"
3254
3261
"and dilate the mask contrast around -4EV\n"
0 commit comments