Skip to content

Commit 33ba3e8

Browse files
Add calculation of capture sharpen radius
1. Add a button beside the radius slider. Pressing that leads to a calculation of a very good guess of the radius to be used. Visual feedback via control log plus setting of the slider via a ui pipe finished signal. 2. You may also set the radius to zero, in that case we do the radius calculation at runtime
1 parent 88a5bcf commit 33ba3e8

File tree

2 files changed

+309
-2
lines changed

2 files changed

+309
-2
lines changed

src/iop/demosaic.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ typedef struct dt_iop_demosaic_gui_data_t
148148
GtkWidget *cs_strength;
149149
gboolean cs_mask;
150150
gboolean dual_mask;
151+
gboolean autoradius;
152+
float newradius;
151153
} dt_iop_demosaic_gui_data_t;
152154

153155
typedef struct dt_iop_demosaic_global_data_t
@@ -1436,6 +1438,9 @@ void gui_update(dt_iop_module_t *self)
14361438
{
14371439
gui_changed(self, NULL, NULL);
14381440
gtk_stack_set_visible_child_name(GTK_STACK(self->widget), self->default_enabled ? "raw" : "non_raw");
1441+
dt_iop_demosaic_gui_data_t *g = self->gui_data;
1442+
g->autoradius = FALSE;
1443+
g->newradius = 0.0f;
14391444
}
14401445

14411446
static void _dual_quad_callback(GtkWidget *quad, dt_iop_module_t *self)
@@ -1461,6 +1466,28 @@ static void _cs_quad_callback(GtkWidget *quad, dt_iop_module_t *self)
14611466
dt_dev_reprocess_center(self->dev);
14621467
}
14631468

1469+
static void _cs_autoradius_callback(GtkWidget *quad, dt_iop_module_t *self)
1470+
{
1471+
if(darktable.gui->reset) return;
1472+
dt_iop_demosaic_gui_data_t *g = self->gui_data;
1473+
g->autoradius = TRUE;
1474+
g->newradius = 2.0f;
1475+
dt_dev_reprocess_center(self->dev);
1476+
}
1477+
1478+
static void _check_autoradius(gpointer instance, dt_iop_module_t *self)
1479+
{
1480+
dt_iop_demosaic_gui_data_t *g = self->gui_data;
1481+
if(g && g->autoradius)
1482+
{
1483+
dt_iop_demosaic_params_t *p = self->params;
1484+
g->autoradius = FALSE;
1485+
p->cs_radius = g->newradius;
1486+
dt_bauhaus_slider_set_val(g->cs_radius, g->newradius);
1487+
dt_dev_add_history_item(darktable.develop, self, TRUE);
1488+
}
1489+
}
1490+
14641491
void gui_focus(dt_iop_module_t *self, gboolean in)
14651492
{
14661493
dt_iop_demosaic_gui_data_t *g = self->gui_data;
@@ -1526,6 +1553,10 @@ void gui_init(dt_iop_module_t *self)
15261553
dt_bauhaus_slider_set_step(g->cs_radius, 0.02f);
15271554
dt_bauhaus_slider_set_format(g->cs_radius, _(_(" px")));
15281555
gtk_widget_set_tooltip_text(g->cs_radius, _("capture sharpen radius. increasing this too far will lead to halos"));
1556+
dt_bauhaus_widget_set_quad(g->cs_radius, self, dtgtk_cairo_paint_reset, FALSE, _cs_autoradius_callback,
1557+
_("approximate a safe radius from sensor data"));
1558+
g->autoradius = FALSE;
1559+
g->newradius = 0.0f;
15291560

15301561
g->cs_thrs = dt_bauhaus_slider_from_params(self, "cs_thrs");
15311562
dt_bauhaus_slider_set_format(g->cs_thrs, "%");
@@ -1549,6 +1580,7 @@ void gui_init(dt_iop_module_t *self)
15491580

15501581
gtk_stack_add_named(GTK_STACK(self->widget), label_non_raw, "non_raw");
15511582
gtk_stack_add_named(GTK_STACK(self->widget), box_raw, "raw");
1583+
DT_CONTROL_SIGNAL_HANDLE(DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED, _check_autoradius);
15521584
}
15531585

15541586
// clang-format off

src/iop/demosaicing/capture.c

Lines changed: 277 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,232 @@ static void _build_blend_mask(float *clipmask,
335335
}
336336
}
337337

338+
#define RAWEPS 0.0001f
339+
float calcRadiusBayer(const float *in,
340+
const int width,
341+
const int height,
342+
const float lowerLimit,
343+
const float upperLimit,
344+
const uint32_t filters)
345+
{
346+
const unsigned int fc[2] = {FC(0, 0, filters), FC(1, 0, filters)};
347+
float maxRatio = 1.f;
348+
DT_OMP_FOR(reduction(max: maxRatio))
349+
for(int row = 4; row < height - 4; ++row)
350+
{
351+
for(int col = 5 + (fc[row & 1] & 1); col < width - 4; col += 2)
352+
{
353+
const float *cfa = in + row*width + col;
354+
const float val00 = cfa[0];
355+
if(val00 > RAWEPS)
356+
{
357+
const float val1m1 = cfa[width-1];
358+
const float val1p1 = cfa[width+1];
359+
const float maxVal0 = MAX(val00, val1m1);
360+
if(val1m1 > RAWEPS && maxVal0 > lowerLimit)
361+
{
362+
const float minVal = MIN(val00, val1m1);
363+
if(maxVal0 > maxRatio * minVal)
364+
{
365+
gboolean clipped = FALSE;
366+
if(maxVal0 == val00)
367+
{ // check for influence by clipped green in neighborhood
368+
if(MAX(MAX(cfa[-width-1], cfa[-width+1]), val1p1) >= upperLimit)
369+
{
370+
clipped = TRUE;
371+
}
372+
}
373+
else
374+
{ // check for influence by clipped green in neighborhood
375+
if(MAX(MAX(MAX(cfa[-2], val00), cfa[2*width-2]), cfa[2*width]) >= upperLimit)
376+
{
377+
clipped = TRUE;
378+
}
379+
}
380+
if(!clipped)
381+
{
382+
maxRatio = maxVal0 / minVal;
383+
}
384+
}
385+
}
386+
387+
const float maxVal1 = MAX(val00, val1p1);
388+
if(val1p1 > RAWEPS && maxVal1 > lowerLimit)
389+
{
390+
const float minVal = MIN(val00, val1p1);
391+
if(maxVal1 > maxRatio * minVal)
392+
{
393+
if(maxVal1 == val00)
394+
{ // check for influence by clipped green in neighborhood
395+
if(MAX(MAX(cfa[-width-1], cfa[-width+1]), val1p1) >= upperLimit)
396+
{
397+
continue;
398+
}
399+
}
400+
else
401+
{ // check for influence by clipped green in neighborhood
402+
if(MAX(MAX(MAX(val00, cfa[2]), cfa[2*width]), cfa[2*width+2]) >= upperLimit)
403+
{
404+
continue;
405+
}
406+
}
407+
maxRatio = maxVal1 / minVal;
408+
}
409+
}
410+
}
411+
}
412+
}
413+
return sqrtf(1.0f / logf(maxRatio));
414+
}
415+
416+
float calcRadiusXtrans(const float *in,
417+
const float lowerLimit,
418+
const float upperLimit,
419+
const dt_iop_roi_t *const roi,
420+
const uint8_t(*const xtrans)[6])
421+
{
422+
const int width = roi->width;
423+
const int height = roi->height;
424+
425+
int startx, starty;
426+
gboolean found = FALSE;
427+
for(starty = 6; starty < 12 && !found; starty++)
428+
{
429+
for(startx = 6; startx < 12 && !found; startx++)
430+
{
431+
if(FCxtrans(starty, startx, roi, xtrans) == 1)
432+
{
433+
if(FCxtrans(starty, startx - 1, roi, xtrans) != FCxtrans(starty, startx + 1, roi, xtrans))
434+
{
435+
if(FCxtrans(starty -1, startx, roi, xtrans) != 1)
436+
{
437+
if(FCxtrans(starty, startx -1, roi, xtrans) != 1)
438+
{
439+
found = TRUE;
440+
break;
441+
}
442+
}
443+
}
444+
}
445+
}
446+
}
447+
448+
float maxRatio = 1.0f;
449+
DT_OMP_FOR(reduction(max: maxRatio))
450+
for(int row = starty + 2; row < height - 4; row += 3)
451+
{
452+
for(int col = startx + 2; col < width - 4; col += 3)
453+
{
454+
const float *cfa = in + row*width + col;
455+
const float valp1p1 = cfa[width+1];
456+
const gboolean squareClipped = MAX(MAX(MAX(valp1p1, cfa[width+2]), cfa[2*width+1]), cfa[2*width+2]) >= upperLimit;
457+
const float greenSolitary = cfa[0];
458+
if(greenSolitary > RAWEPS && MAX(cfa[-width-1], cfa[-width+1]) < upperLimit)
459+
{
460+
if(greenSolitary < upperLimit)
461+
{
462+
const float valp1m1 = cfa[width-1];
463+
if(valp1m1 > RAWEPS && MAX(MAX(MAX(cfa[width-2], valp1m1), cfa[2*width-2]), cfa[width-1]) < upperLimit)
464+
{
465+
const float maxVal = MAX(greenSolitary, valp1m1);
466+
if(maxVal > lowerLimit)
467+
{
468+
const float minVal = MIN(greenSolitary, valp1m1);
469+
if(maxVal > maxRatio * minVal)
470+
{
471+
maxRatio = maxVal / minVal;
472+
}
473+
}
474+
}
475+
if(valp1p1 > RAWEPS && !squareClipped)
476+
{
477+
const float maxVal = MAX(greenSolitary, valp1p1);
478+
if(maxVal > lowerLimit)
479+
{
480+
const float minVal = MIN(greenSolitary, valp1p1);
481+
if(maxVal > maxRatio * minVal)
482+
{
483+
maxRatio = maxVal / minVal;
484+
}
485+
}
486+
}
487+
}
488+
}
489+
490+
if(!squareClipped)
491+
{
492+
const float valp2p2 = cfa[2*width+2];
493+
if(valp2p2 > RAWEPS)
494+
{
495+
if(valp1p1 > RAWEPS)
496+
{
497+
const float maxVal = MAX(valp1p1, valp2p2);
498+
if(maxVal > lowerLimit)
499+
{
500+
const float minVal = MIN(valp1p1, valp2p2);
501+
if(maxVal > maxRatio * minVal)
502+
{
503+
maxRatio = maxVal / minVal;
504+
}
505+
}
506+
}
507+
const float greenSolitaryRight = cfa[3*width+3];
508+
if(MAX(MAX(greenSolitaryRight, cfa[4*width+2]), cfa[4*width+4]) < upperLimit)
509+
{
510+
if(greenSolitaryRight > RAWEPS)
511+
{
512+
const float maxVal = MAX(greenSolitaryRight, valp2p2);
513+
if(maxVal > lowerLimit)
514+
{
515+
const float minVal = MIN(greenSolitaryRight, valp2p2);
516+
if(maxVal > maxRatio * minVal)
517+
{
518+
maxRatio = maxVal / minVal;
519+
}
520+
}
521+
}
522+
}
523+
}
524+
const float valp1p2 = cfa[width+2];
525+
const float valp2p1 = cfa[2*width+1];
526+
if(valp2p1 > RAWEPS)
527+
{
528+
if(valp1p2 > RAWEPS)
529+
{
530+
const float maxVal = MAX(valp1p2, valp2p1);
531+
if(maxVal > lowerLimit)
532+
{
533+
const float minVal = MIN(valp1p2, valp2p1);
534+
if(maxVal > maxRatio * minVal)
535+
{
536+
maxRatio = maxVal / minVal;
537+
}
538+
}
539+
}
540+
const float greenSolitaryLeft = cfa[3*width];
541+
if(MAX(MAX(greenSolitaryLeft, cfa[4*width-1]), cfa[4*width+1]) < upperLimit)
542+
{
543+
if(greenSolitaryLeft > RAWEPS)
544+
{
545+
const float maxVal = MAX(greenSolitaryLeft, valp2p1);
546+
if(maxVal > lowerLimit)
547+
{
548+
const float minVal = MIN(greenSolitaryLeft, valp2p1);
549+
if(maxVal > maxRatio * minVal)
550+
{
551+
maxRatio = maxVal / minVal;
552+
}
553+
}
554+
}
555+
}
556+
}
557+
}
558+
}
559+
}
560+
return sqrtf(1.0f / logf(maxRatio));
561+
}
562+
#undef RAWEPS
563+
338564
void _capture_sharpen(dt_iop_module_t *self,
339565
dt_dev_pixelpipe_iop_t *piece,
340566
float *in,
@@ -350,6 +576,7 @@ void _capture_sharpen(dt_iop_module_t *self,
350576
const size_t pixels = width * height;
351577
const dt_iop_demosaic_data_t *d = piece->data;
352578
const dt_iop_demosaic_global_data_t *gd = self->global_data;
579+
dt_iop_demosaic_gui_data_t *g = self->gui_data;
353580

354581
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
355582
const uint32_t filters = piece->pipe->dsc.filters;
@@ -359,6 +586,26 @@ void _capture_sharpen(dt_iop_module_t *self,
359586
wbon ? dsc->temperature.coeffs[1] : 1.0f,
360587
wbon ? dsc->temperature.coeffs[2] : 1.0f,
361588
0.0f };
589+
const gboolean autoradius = (pipe->type & DT_DEV_PIXELPIPE_FULL)
590+
&& g && g->autoradius;
591+
float radius = d->cs_radius;
592+
const gboolean noradius = radius < 0.01f;
593+
if(autoradius || noradius)
594+
{
595+
radius = filters != 9u
596+
? calcRadiusBayer(in, width, height, 0.01f, 1.0f, filters)
597+
: calcRadiusXtrans(in, 0.01f, 1.0f, roi, xtrans);
598+
599+
dt_print_pipe(DT_DEBUG_PIPE, filters != 9u ? "bayer autoradius" : "xtrans autoradius",
600+
pipe, self, DT_DEVICE_CPU, roi, NULL, "autoradius=%.2f", radius);
601+
if(autoradius)
602+
{
603+
dt_control_log(_("calculated radius: %.2f"), radius);
604+
dt_iop_gui_enter_critical_section(self);
605+
g->newradius = MIN(g->newradius, radius);
606+
dt_iop_gui_leave_critical_section(self);
607+
}
608+
}
362609

363610
char *gauss_idx = NULL;
364611
gboolean error = TRUE;
@@ -385,7 +632,7 @@ void _capture_sharpen(dt_iop_module_t *self,
385632
goto finalize;
386633
}
387634

388-
gauss_idx = _cs_precalc_gauss_idx(self, roi, d->cs_radius, d->cs_boost);
635+
gauss_idx = _cs_precalc_gauss_idx(self, roi, radius, d->cs_boost);
389636
if(!gauss_idx) goto finalize;
390637

391638
for(int iter = 0; iter < iterations; iter++)
@@ -434,6 +681,7 @@ int _capture_sharpen_cl(dt_iop_module_t *self,
434681
const int devid = piece->pipe->devid;
435682
const dt_iop_demosaic_data_t *d = piece->data;
436683
dt_iop_demosaic_global_data_t *const gd = self->global_data;
684+
dt_iop_demosaic_gui_data_t *g = self->gui_data;
437685

438686
const float threshold = 0.09f * d->cs_thrs;
439687
const uint32_t filters = piece->pipe->dsc.filters;
@@ -443,6 +691,33 @@ int _capture_sharpen_cl(dt_iop_module_t *self,
443691
wbon ? dsc->temperature.coeffs[1] : 1.0f,
444692
wbon ? dsc->temperature.coeffs[2] : 1.0f,
445693
0.0f };
694+
const gboolean autoradius = (pipe->type & DT_DEV_PIXELPIPE_FULL)
695+
&& g && g->autoradius;
696+
float radius = d->cs_radius;
697+
const gboolean noradius = radius < 0.01f;
698+
if(autoradius || noradius)
699+
{
700+
float *in = dt_alloc_align_float((size_t)width * height);
701+
if(in)
702+
{
703+
if(dt_opencl_read_host_from_device(devid, in, dev_in, width, height, sizeof(float)) == CL_SUCCESS)
704+
{
705+
radius = filters != 9u
706+
? calcRadiusBayer(in, width, height, 0.01f, 1.0f, filters)
707+
: calcRadiusXtrans(in, 0.01f, 1.0f, roi, (const uint8_t(*const)[6])piece->pipe->dsc.xtrans);
708+
dt_print_pipe(DT_DEBUG_PIPE, filters != 9u ? "bayer autoradius" : "xtrans autoradius",
709+
pipe, self, devid, roi, NULL, "autoradius=%.2f", radius);
710+
if(autoradius)
711+
{
712+
dt_control_log(_("calculated radius: %.2f"), radius);
713+
dt_iop_gui_enter_critical_section(self);
714+
g->newradius = MIN(g->newradius, radius);
715+
dt_iop_gui_leave_critical_section(self);
716+
}
717+
}
718+
dt_free_align(in);
719+
}
720+
}
446721

447722
cl_mem gcoeffs = NULL;
448723
cl_mem gauss_idx = NULL;
@@ -481,7 +756,7 @@ int _capture_sharpen_cl(dt_iop_module_t *self,
481756
goto finish;
482757
}
483758

484-
char *f_gauss_idx = _cs_precalc_gauss_idx(self, roi, d->cs_radius, d->cs_boost);
759+
char *f_gauss_idx = _cs_precalc_gauss_idx(self, roi, radius, d->cs_boost);
485760
if(f_gauss_idx)
486761
{
487762
gcoeffs = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * CHAR_MAX * CAPTURE_KERNEL_ALIGN, gd->gauss_coeffs);

0 commit comments

Comments
 (0)