Skip to content

Commit ff2ef23

Browse files
authored
Merge pull request #17682 from jenshannoschwalm/linear_response_limit
Introduce LinearResponseLimit
2 parents b080013 + 997e5d1 commit ff2ef23

File tree

6 files changed

+80
-55
lines changed

6 files changed

+80
-55
lines changed

src/common/exif.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,26 @@ static bool _check_usercrop(Exiv2::ExifData &exifData,
858858
return FALSE;
859859
}
860860

861+
static void _check_linear_response_limit(Exiv2::ExifData &exifData,
862+
dt_image_t *img)
863+
{
864+
bool found_one = false;
865+
866+
// currently this only reads the dng tag, could also be used for other raws
867+
868+
Exiv2::ExifData::const_iterator linr =
869+
exifData.findKey(Exiv2::ExifKey("Exif.Image.LinearResponseLimit"));
870+
if(linr != exifData.end() && linr->count() == 1)
871+
{
872+
img->linear_response_limit = linr->toFloat();
873+
found_one = true;
874+
}
875+
876+
if(found_one)
877+
dt_print(DT_DEBUG_IMAGEIO, "[exif] `%s` has LinearResponseLimit %.4f",
878+
img->filename, img->linear_response_limit);
879+
}
880+
861881
static gboolean _check_dng_opcodes(Exiv2::ExifData &exifData,
862882
dt_image_t *img)
863883
{
@@ -1074,13 +1094,14 @@ void dt_exif_img_check_additional_tags(dt_image_t *img,
10741094
_check_usercrop(exifData, img);
10751095
_check_dng_opcodes(exifData, img);
10761096
_check_lens_correction_data(exifData, img);
1097+
_check_linear_response_limit(exifData, img);
10771098
}
10781099
return;
10791100
}
10801101
catch(Exiv2::AnyError &e)
10811102
{
10821103
const char *errstring = e.what();
1083-
dt_print(DT_DEBUG_IMAGEIO, "[exiv2 reading DefaultUserCrop] %s: %s", filename, errstring);
1104+
dt_print(DT_DEBUG_IMAGEIO, "[exiv2 reading additional exif tags] %s: %s", filename, errstring);
10841105
return;
10851106
}
10861107
}

src/common/image.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,7 @@ void dt_image_init(dt_image_t *img)
21322132
img->colorspace = DT_IMAGE_COLORSPACE_NONE;
21332133
img->fuji_rotation_pos = 0;
21342134
img->pixel_aspect_ratio = 1.0f;
2135+
img->linear_response_limit = 1.0f;
21352136
img->wb_coeffs[0] = NAN;
21362137
img->wb_coeffs[1] = NAN;
21372138
img->wb_coeffs[2] = NAN;

src/common/image.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ typedef struct dt_image_t
336336
uint32_t fuji_rotation_pos;
337337
float pixel_aspect_ratio;
338338

339+
/* might help to improve highlights reconstruction module */
340+
float linear_response_limit;
341+
339342
/* White balance coeffs from the raw */
340343
dt_aligned_pixel_t wb_coeffs;
341344

src/iop/highlights.c

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -385,11 +385,11 @@ void modify_roi_in(dt_iop_module_t *self,
385385
roi_in->scale = 1.0f;
386386
}
387387

388-
void tiling_callback(struct dt_iop_module_t *self,
389-
struct dt_dev_pixelpipe_iop_t *piece,
388+
void tiling_callback(dt_iop_module_t *self,
389+
dt_dev_pixelpipe_iop_t *piece,
390390
const dt_iop_roi_t *roi_in,
391391
const dt_iop_roi_t *roi_out,
392-
struct dt_develop_tiling_t *tiling)
392+
dt_develop_tiling_t *tiling)
393393
{
394394
dt_iop_highlights_data_t *d = piece->data;
395395
const uint32_t filters = piece->pipe->dsc.filters;
@@ -407,8 +407,7 @@ void tiling_callback(struct dt_iop_module_t *self,
407407
tiling->overhead = 0;
408408
tiling->overlap = 0;
409409

410-
dt_develop_blend_params_t *const bldata =
411-
(dt_develop_blend_params_t *const)piece->blendop_data;
410+
dt_develop_blend_params_t *const bldata = piece->blendop_data;
412411
if(bldata
413412
&& (piece->pipe->store_all_raster_masks || dt_iop_is_raster_mask_used(self, BLEND_RASTER_ID)))
414413
{
@@ -464,7 +463,7 @@ void tiling_callback(struct dt_iop_module_t *self,
464463
}
465464

466465
#ifdef HAVE_OPENCL
467-
int process_cl(struct dt_iop_module_t *self,
466+
int process_cl(dt_iop_module_t *self,
468467
dt_dev_pixelpipe_iop_t *piece,
469468
cl_mem dev_in,
470469
cl_mem dev_out,
@@ -673,15 +672,15 @@ static void process_visualize(dt_dev_pixelpipe_iop_t *piece,
673672
void *const ovoid,
674673
const dt_iop_roi_t *const roi_in,
675674
const dt_iop_roi_t *const roi_out,
676-
dt_iop_highlights_data_t *data)
675+
dt_iop_highlights_data_t *d)
677676
{
678677
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
679678
const uint32_t filters = piece->pipe->dsc.filters;
680679
const gboolean is_xtrans = (filters == 9u);
681680
const float *const in = (const float *const)ivoid;
682681
float *const out = (float *const)ovoid;
683682

684-
const float mclip = data->clip * highlights_clip_magics[data->mode];
683+
const float mclip = d->clip * highlights_clip_magics[d->mode];
685684
const float *cf = piece->pipe->dsc.temperature.coeffs;
686685
const float clips[4] = { mclip * (cf[RED] <= 0.0f ? 1.0f : cf[RED]),
687686
mclip * (cf[GREEN] <= 0.0f ? 1.0f : cf[GREEN]),
@@ -724,27 +723,28 @@ static void process_visualize(dt_dev_pixelpipe_iop_t *piece,
724723
}
725724
}
726725

727-
void process(struct dt_iop_module_t *self,
726+
void process(dt_iop_module_t *self,
728727
dt_dev_pixelpipe_iop_t *piece,
729728
const void *const ivoid,
730729
void *const ovoid,
731730
const dt_iop_roi_t *const roi_in,
732731
const dt_iop_roi_t *const roi_out)
733732
{
734733
const uint32_t filters = piece->pipe->dsc.filters;
735-
dt_iop_highlights_data_t *data = piece->data;
734+
dt_iop_highlights_data_t *d = piece->data;
736735
dt_iop_highlights_gui_data_t *g = self->gui_data;
737736

738737
const gboolean fullpipe = piece->pipe->type & DT_DEV_PIXELPIPE_FULL;
739738
const gboolean fastmode = piece->pipe->type & DT_DEV_PIXELPIPE_FAST;
739+
740740
if(g && fullpipe)
741741
{
742742
if(g->hlr_mask_mode != DT_HIGHLIGHTS_MASK_OFF)
743743
{
744744
piece->pipe->mask_display = DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU;
745745
if(g->hlr_mask_mode == DT_HIGHLIGHTS_MASK_CLIPPED)
746746
{
747-
process_visualize(piece, ivoid, ovoid, roi_in, roi_out, data);
747+
process_visualize(piece, ivoid, ovoid, roi_in, roi_out, d);
748748
return;
749749
}
750750
}
@@ -760,11 +760,11 @@ void process(struct dt_iop_module_t *self,
760760
high_quality = (level >= min_s);
761761
}
762762

763-
const float clip = data->clip * dt_iop_get_processed_minimum(piece);
763+
const float clip = d->clip * dt_iop_get_processed_minimum(piece);
764764

765765
if(filters == 0)
766766
{
767-
if(data->mode == DT_IOP_HIGHLIGHTS_CLIP)
767+
if(d->mode == DT_IOP_HIGHLIGHTS_CLIP)
768768
{
769769
process_clip(self, piece, ivoid, ovoid, roi_in, roi_out, clip);
770770
const float m = dt_iop_get_processed_minimum(piece);
@@ -778,13 +778,13 @@ void process(struct dt_iop_module_t *self,
778778
return;
779779
}
780780

781-
const dt_iop_highlights_mode_t dmode = fastmode && (data->mode == DT_IOP_HIGHLIGHTS_SEGMENTS)
782-
? DT_IOP_HIGHLIGHTS_OPPOSED : data->mode;
781+
const dt_iop_highlights_mode_t dmode = fastmode && (d->mode == DT_IOP_HIGHLIGHTS_SEGMENTS)
782+
? DT_IOP_HIGHLIGHTS_OPPOSED : d->mode;
783783
switch(dmode)
784784
{
785785
case DT_IOP_HIGHLIGHTS_INPAINT: // a1ex's (magiclantern) idea of color inpainting:
786786
{
787-
const float clipper = data->clip * highlights_clip_magics[DT_IOP_HIGHLIGHTS_INPAINT];
787+
const float clipper = d->clip * highlights_clip_magics[DT_IOP_HIGHLIGHTS_INPAINT];
788788
const float clips[4] = { clipper * piece->pipe->dsc.processed_maximum[0],
789789
clipper * piece->pipe->dsc.processed_maximum[1],
790790
clipper * piece->pipe->dsc.processed_maximum[2], clip };
@@ -840,7 +840,7 @@ void process(struct dt_iop_module_t *self,
840840

841841
float *tmp = _process_opposed(self, piece, ivoid, ovoid, roi_in, roi_out, TRUE, TRUE);
842842
if(tmp)
843-
_process_segmentation(piece, ivoid, ovoid, roi_in, roi_out, data, vmode, tmp);
843+
_process_segmentation(piece, ivoid, ovoid, roi_in, roi_out, d, vmode, tmp);
844844
dt_free_align(tmp);
845845
break;
846846
}
@@ -853,7 +853,7 @@ void process(struct dt_iop_module_t *self,
853853

854854
case DT_IOP_HIGHLIGHTS_LAPLACIAN:
855855
{
856-
const float clipper = data->clip * highlights_clip_magics[DT_IOP_HIGHLIGHTS_LAPLACIAN];
856+
const float clipper = d->clip * highlights_clip_magics[DT_IOP_HIGHLIGHTS_LAPLACIAN];
857857
const dt_aligned_pixel_t clips = { clipper * piece->pipe->dsc.processed_maximum[0],
858858
clipper * piece->pipe->dsc.processed_maximum[1],
859859
clipper * piece->pipe->dsc.processed_maximum[2], clip };
@@ -869,7 +869,7 @@ void process(struct dt_iop_module_t *self,
869869
}
870870

871871
// update processed maximum
872-
if((data->mode != DT_IOP_HIGHLIGHTS_LAPLACIAN) && (data->mode != DT_IOP_HIGHLIGHTS_SEGMENTS) && (data->mode != DT_IOP_HIGHLIGHTS_OPPOSED))
872+
if((d->mode != DT_IOP_HIGHLIGHTS_LAPLACIAN) && (d->mode != DT_IOP_HIGHLIGHTS_SEGMENTS) && (d->mode != DT_IOP_HIGHLIGHTS_OPPOSED))
873873
{
874874
// The guided laplacian, inpaint opposed and segmentation modes keep signal scene-referred and don't clip highlights to 1
875875
// For the other modes, we need to notify the pipeline that white point has changed
@@ -878,7 +878,7 @@ void process(struct dt_iop_module_t *self,
878878
}
879879
}
880880

881-
void commit_params(struct dt_iop_module_t *self,
881+
void commit_params(dt_iop_module_t *self,
882882
dt_iop_params_t *p1,
883883
dt_dev_pixelpipe_t *pipe,
884884
dt_dev_pixelpipe_iop_t *piece)
@@ -919,8 +919,7 @@ void commit_params(struct dt_iop_module_t *self,
919919
void init_global(dt_iop_module_so_t *module)
920920
{
921921
const int program = 2; // basic.cl, from programs.conf
922-
dt_iop_highlights_global_data_t *gd
923-
= (dt_iop_highlights_global_data_t *)malloc(sizeof(dt_iop_highlights_global_data_t));
922+
dt_iop_highlights_global_data_t *gd = malloc(sizeof(dt_iop_highlights_global_data_t));
924923
module->data = gd;
925924
gd->kernel_highlights_1f_clip = dt_opencl_create_kernel(program, "highlights_1f_clip");
926925
gd->kernel_highlights_1f_lch_bayer = dt_opencl_create_kernel(program, "highlights_1f_lch_bayer");
@@ -1050,7 +1049,7 @@ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
10501049
}
10511050
}
10521051

1053-
void gui_update(struct dt_iop_module_t *self)
1052+
void gui_update(dt_iop_module_t *self)
10541053
{
10551054
dt_iop_highlights_gui_data_t *g = self->gui_data;
10561055
const dt_image_t *img = &self->dev->image_storage;
@@ -1124,6 +1123,7 @@ void reload_defaults(dt_iop_module_t *self)
11241123
dt_bauhaus_widget_set_quad_active(g->strength, FALSE);
11251124
g->hlr_mask_mode = DT_HIGHLIGHTS_MASK_OFF;
11261125
}
1126+
d->clip = MIN(d->clip, img->linear_response_limit);
11271127
}
11281128

11291129
static void _visualize_callback(GtkWidget *quad, dt_iop_module_t *self)

src/iop/hlreconstruct/opposed.c

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,13 @@ static inline char _mask_dilated(const char *in, const size_t w1)
9191

9292

9393
// A slightly modified version for sraws
94-
static void _process_linear_opposed(
95-
struct dt_iop_module_t *self,
96-
dt_dev_pixelpipe_iop_t *piece,
97-
const float *const input,
98-
float *const output,
99-
const dt_iop_roi_t *const roi_in,
100-
const dt_iop_roi_t *const roi_out,
101-
const gboolean quality)
94+
static void _process_linear_opposed(dt_iop_module_t *self,
95+
dt_dev_pixelpipe_iop_t *piece,
96+
const float *const input,
97+
float *const output,
98+
const dt_iop_roi_t *const roi_in,
99+
const dt_iop_roi_t *const roi_out,
100+
const gboolean quality)
102101
{
103102
dt_iop_highlights_data_t *d = piece->data;
104103
const float clipval = highlights_clip_magics[DT_IOP_HIGHLIGHTS_OPPOSED] * d->clip;
@@ -203,20 +202,20 @@ static void _process_linear_opposed(
203202
}
204203
}
205204

206-
static float *_process_opposed(
207-
struct dt_iop_module_t *self,
208-
dt_dev_pixelpipe_iop_t *piece,
209-
const float *const input,
210-
float *const output,
211-
const dt_iop_roi_t *const roi_in,
212-
const dt_iop_roi_t *const roi_out,
213-
const gboolean keep,
214-
const gboolean quality)
205+
static float *_process_opposed(dt_iop_module_t *self,
206+
dt_dev_pixelpipe_iop_t *piece,
207+
const float *const input,
208+
float *const output,
209+
const dt_iop_roi_t *const roi_in,
210+
const dt_iop_roi_t *const roi_out,
211+
const gboolean keep,
212+
const gboolean quality)
215213
{
216214
dt_iop_highlights_data_t *d = piece->data;
217215
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
218216
const uint32_t filters = piece->pipe->dsc.filters;
219217
const float clipval = highlights_clip_magics[DT_IOP_HIGHLIGHTS_OPPOSED] * d->clip;
218+
220219
const dt_iop_buffer_dsc_t *dsc = &piece->pipe->dsc;
221220
const gboolean wbon = dsc->temperature.enabled;
222221
const dt_aligned_pixel_t icoeffs = { wbon ? dsc->temperature.coeffs[0] : 1.0f,
@@ -398,19 +397,19 @@ static float *_process_opposed(
398397
}
399398

400399
#ifdef HAVE_OPENCL
401-
static cl_int process_opposed_cl(
402-
struct dt_iop_module_t *self,
403-
dt_dev_pixelpipe_iop_t *piece,
404-
cl_mem dev_in,
405-
cl_mem dev_out,
406-
const dt_iop_roi_t *const roi_in,
407-
const dt_iop_roi_t *const roi_out)
400+
static cl_int process_opposed_cl(dt_iop_module_t *self,
401+
dt_dev_pixelpipe_iop_t *piece,
402+
cl_mem dev_in,
403+
cl_mem dev_out,
404+
const dt_iop_roi_t *const roi_in,
405+
const dt_iop_roi_t *const roi_out)
408406
{
409407
dt_iop_highlights_data_t *d = piece->data;
410408
const dt_iop_highlights_global_data_t *gd = self->global_data;
411409

412410
const int devid = piece->pipe->devid;
413411
const uint32_t filters = piece->pipe->dsc.filters;
412+
414413
const float clipval = highlights_clip_magics[DT_IOP_HIGHLIGHTS_OPPOSED] * d->clip;
415414
const dt_iop_buffer_dsc_t *dsc = &piece->pipe->dsc;
416415
const gboolean wbon = dsc->temperature.enabled;

src/iop/hlreconstruct/segbased.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -453,14 +453,15 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
453453
float *const output,
454454
const dt_iop_roi_t *const roi_in,
455455
const dt_iop_roi_t *const roi_out,
456-
dt_iop_highlights_data_t *data,
456+
dt_iop_highlights_data_t *d,
457457
const int vmode,
458458
float *tmpout)
459459
{
460460
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
461461
const uint32_t filters = piece->pipe->dsc.filters;
462462
const gboolean fullpipe = piece->pipe->type & DT_DEV_PIXELPIPE_FULL;
463-
const float clipval = fmaxf(0.1f, 0.987f * data->clip);
463+
const float clipval = MAX(0.1f, highlights_clip_magics[DT_IOP_HIGHLIGHTS_SEGMENTS] * d->clip);
464+
464465
const dt_aligned_pixel_t icoeffs = { piece->pipe->dsc.temperature.coeffs[0], piece->pipe->dsc.temperature.coeffs[1], piece->pipe->dsc.temperature.coeffs[2]};
465466
const dt_aligned_pixel_t clips = { clipval * icoeffs[0], clipval * icoeffs[1], clipval * icoeffs[2]};
466467
const dt_aligned_pixel_t cube_coeffs = { powf(clips[0], 1.0f / HL_POWERF), powf(clips[1], 1.0f / HL_POWERF), powf(clips[2], 1.0f / HL_POWERF)};
@@ -471,8 +472,8 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
471472
late ? (float)(chr->D65coeffs[1] / chr->as_shot[1]) : 1.0f,
472473
late ? (float)(chr->D65coeffs[2] / chr->as_shot[2]) : 1.0f,
473474
1.0f };
474-
const int recovery_mode = data->recovery;
475-
const float strength = data->strength;
475+
const int recovery_mode = d->recovery;
476+
const float strength = d->strength;
476477

477478
const int recovery_closing[NUM_RECOVERY_MODES] = { 0, 0, 0, 2, 2, 0, 2};
478479
const int recovery_close = recovery_closing[recovery_mode];
@@ -574,7 +575,7 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
574575
_masks_extend_border(plane[i], pwidth, pheight, HL_BORDER);
575576

576577
for(int p = 0; p < HL_RGB_PLANES; p++)
577-
dt_segments_combine(&isegments[p], data->combine);
578+
dt_segments_combine(&isegments[p], d->combine);
578579

579580
if(dt_get_num_threads() >= HL_RGB_PLANES)
580581
{
@@ -590,7 +591,7 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
590591
}
591592

592593
for(int p = 0; p < HL_RGB_PLANES; p++)
593-
_calc_plane_candidates(plane[p], refavg[p], &isegments[p], cube_coeffs[p], data->candidating);
594+
_calc_plane_candidates(plane[p], refavg[p], &isegments[p], cube_coeffs[p], d->candidating);
594595

595596
DT_OMP_FOR(collapse(2))
596597
for(int row = 1; row < roi_in->height-1; row++)
@@ -670,7 +671,7 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
670671

671672
dt_gaussian_fast_blur(recout, gradient, pwidth, pheight, 1.2f, 0.0f, 20.0f, 1);
672673
// possibly add some noise
673-
const float noise_level = data->noise_level;
674+
const float noise_level = d->noise_level;
674675
if(noise_level > 0.0f)
675676
{
676677
for(uint32_t id = 2; id < segall->nr; id++)

0 commit comments

Comments
 (0)