Skip to content

Commit

Permalink
Merge pull request #17682 from jenshannoschwalm/linear_response_limit
Browse files Browse the repository at this point in the history
Introduce LinearResponseLimit
  • Loading branch information
TurboGit authored Oct 21, 2024
2 parents b080013 + 997e5d1 commit ff2ef23
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 55 deletions.
23 changes: 22 additions & 1 deletion src/common/exif.cc
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,26 @@ static bool _check_usercrop(Exiv2::ExifData &exifData,
return FALSE;
}

static void _check_linear_response_limit(Exiv2::ExifData &exifData,
dt_image_t *img)
{
bool found_one = false;

// currently this only reads the dng tag, could also be used for other raws

Exiv2::ExifData::const_iterator linr =
exifData.findKey(Exiv2::ExifKey("Exif.Image.LinearResponseLimit"));
if(linr != exifData.end() && linr->count() == 1)
{
img->linear_response_limit = linr->toFloat();
found_one = true;
}

if(found_one)
dt_print(DT_DEBUG_IMAGEIO, "[exif] `%s` has LinearResponseLimit %.4f",
img->filename, img->linear_response_limit);
}

static gboolean _check_dng_opcodes(Exiv2::ExifData &exifData,
dt_image_t *img)
{
Expand Down Expand Up @@ -1074,13 +1094,14 @@ void dt_exif_img_check_additional_tags(dt_image_t *img,
_check_usercrop(exifData, img);
_check_dng_opcodes(exifData, img);
_check_lens_correction_data(exifData, img);
_check_linear_response_limit(exifData, img);
}
return;
}
catch(Exiv2::AnyError &e)
{
const char *errstring = e.what();
dt_print(DT_DEBUG_IMAGEIO, "[exiv2 reading DefaultUserCrop] %s: %s", filename, errstring);
dt_print(DT_DEBUG_IMAGEIO, "[exiv2 reading additional exif tags] %s: %s", filename, errstring);
return;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/common/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -2132,6 +2132,7 @@ void dt_image_init(dt_image_t *img)
img->colorspace = DT_IMAGE_COLORSPACE_NONE;
img->fuji_rotation_pos = 0;
img->pixel_aspect_ratio = 1.0f;
img->linear_response_limit = 1.0f;
img->wb_coeffs[0] = NAN;
img->wb_coeffs[1] = NAN;
img->wb_coeffs[2] = NAN;
Expand Down
3 changes: 3 additions & 0 deletions src/common/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,9 @@ typedef struct dt_image_t
uint32_t fuji_rotation_pos;
float pixel_aspect_ratio;

/* might help to improve highlights reconstruction module */
float linear_response_limit;

/* White balance coeffs from the raw */
dt_aligned_pixel_t wb_coeffs;

Expand Down
46 changes: 23 additions & 23 deletions src/iop/highlights.c
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,11 @@ void modify_roi_in(dt_iop_module_t *self,
roi_in->scale = 1.0f;
}

void tiling_callback(struct dt_iop_module_t *self,
struct dt_dev_pixelpipe_iop_t *piece,
void tiling_callback(dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
const dt_iop_roi_t *roi_in,
const dt_iop_roi_t *roi_out,
struct dt_develop_tiling_t *tiling)
dt_develop_tiling_t *tiling)
{
dt_iop_highlights_data_t *d = piece->data;
const uint32_t filters = piece->pipe->dsc.filters;
Expand All @@ -407,8 +407,7 @@ void tiling_callback(struct dt_iop_module_t *self,
tiling->overhead = 0;
tiling->overlap = 0;

dt_develop_blend_params_t *const bldata =
(dt_develop_blend_params_t *const)piece->blendop_data;
dt_develop_blend_params_t *const bldata = piece->blendop_data;
if(bldata
&& (piece->pipe->store_all_raster_masks || dt_iop_is_raster_mask_used(self, BLEND_RASTER_ID)))
{
Expand Down Expand Up @@ -464,7 +463,7 @@ void tiling_callback(struct dt_iop_module_t *self,
}

#ifdef HAVE_OPENCL
int process_cl(struct dt_iop_module_t *self,
int process_cl(dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
cl_mem dev_in,
cl_mem dev_out,
Expand Down Expand Up @@ -673,15 +672,15 @@ static void process_visualize(dt_dev_pixelpipe_iop_t *piece,
void *const ovoid,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out,
dt_iop_highlights_data_t *data)
dt_iop_highlights_data_t *d)
{
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
const uint32_t filters = piece->pipe->dsc.filters;
const gboolean is_xtrans = (filters == 9u);
const float *const in = (const float *const)ivoid;
float *const out = (float *const)ovoid;

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

void process(struct dt_iop_module_t *self,
void process(dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
const void *const ivoid,
void *const ovoid,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out)
{
const uint32_t filters = piece->pipe->dsc.filters;
dt_iop_highlights_data_t *data = piece->data;
dt_iop_highlights_data_t *d = piece->data;
dt_iop_highlights_gui_data_t *g = self->gui_data;

const gboolean fullpipe = piece->pipe->type & DT_DEV_PIXELPIPE_FULL;
const gboolean fastmode = piece->pipe->type & DT_DEV_PIXELPIPE_FAST;

if(g && fullpipe)
{
if(g->hlr_mask_mode != DT_HIGHLIGHTS_MASK_OFF)
{
piece->pipe->mask_display = DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU;
if(g->hlr_mask_mode == DT_HIGHLIGHTS_MASK_CLIPPED)
{
process_visualize(piece, ivoid, ovoid, roi_in, roi_out, data);
process_visualize(piece, ivoid, ovoid, roi_in, roi_out, d);
return;
}
}
Expand All @@ -760,11 +760,11 @@ void process(struct dt_iop_module_t *self,
high_quality = (level >= min_s);
}

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

if(filters == 0)
{
if(data->mode == DT_IOP_HIGHLIGHTS_CLIP)
if(d->mode == DT_IOP_HIGHLIGHTS_CLIP)
{
process_clip(self, piece, ivoid, ovoid, roi_in, roi_out, clip);
const float m = dt_iop_get_processed_minimum(piece);
Expand All @@ -778,13 +778,13 @@ void process(struct dt_iop_module_t *self,
return;
}

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

float *tmp = _process_opposed(self, piece, ivoid, ovoid, roi_in, roi_out, TRUE, TRUE);
if(tmp)
_process_segmentation(piece, ivoid, ovoid, roi_in, roi_out, data, vmode, tmp);
_process_segmentation(piece, ivoid, ovoid, roi_in, roi_out, d, vmode, tmp);
dt_free_align(tmp);
break;
}
Expand All @@ -853,7 +853,7 @@ void process(struct dt_iop_module_t *self,

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

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

void commit_params(struct 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)
Expand Down Expand Up @@ -919,8 +919,7 @@ void commit_params(struct dt_iop_module_t *self,
void init_global(dt_iop_module_so_t *module)
{
const int program = 2; // basic.cl, from programs.conf
dt_iop_highlights_global_data_t *gd
= (dt_iop_highlights_global_data_t *)malloc(sizeof(dt_iop_highlights_global_data_t));
dt_iop_highlights_global_data_t *gd = malloc(sizeof(dt_iop_highlights_global_data_t));
module->data = gd;
gd->kernel_highlights_1f_clip = dt_opencl_create_kernel(program, "highlights_1f_clip");
gd->kernel_highlights_1f_lch_bayer = dt_opencl_create_kernel(program, "highlights_1f_lch_bayer");
Expand Down Expand Up @@ -1050,7 +1049,7 @@ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
}
}

void gui_update(struct dt_iop_module_t *self)
void gui_update(dt_iop_module_t *self)
{
dt_iop_highlights_gui_data_t *g = self->gui_data;
const dt_image_t *img = &self->dev->image_storage;
Expand Down Expand Up @@ -1124,6 +1123,7 @@ void reload_defaults(dt_iop_module_t *self)
dt_bauhaus_widget_set_quad_active(g->strength, FALSE);
g->hlr_mask_mode = DT_HIGHLIGHTS_MASK_OFF;
}
d->clip = MIN(d->clip, img->linear_response_limit);
}

static void _visualize_callback(GtkWidget *quad, dt_iop_module_t *self)
Expand Down
47 changes: 23 additions & 24 deletions src/iop/hlreconstruct/opposed.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,13 @@ static inline char _mask_dilated(const char *in, const size_t w1)


// A slightly modified version for sraws
static void _process_linear_opposed(
struct dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
const float *const input,
float *const output,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out,
const gboolean quality)
static void _process_linear_opposed(dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
const float *const input,
float *const output,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out,
const gboolean quality)
{
dt_iop_highlights_data_t *d = piece->data;
const float clipval = highlights_clip_magics[DT_IOP_HIGHLIGHTS_OPPOSED] * d->clip;
Expand Down Expand Up @@ -203,20 +202,20 @@ static void _process_linear_opposed(
}
}

static float *_process_opposed(
struct dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
const float *const input,
float *const output,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out,
const gboolean keep,
const gboolean quality)
static float *_process_opposed(dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
const float *const input,
float *const output,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out,
const gboolean keep,
const gboolean quality)
{
dt_iop_highlights_data_t *d = piece->data;
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
const uint32_t filters = piece->pipe->dsc.filters;
const float clipval = highlights_clip_magics[DT_IOP_HIGHLIGHTS_OPPOSED] * d->clip;

const dt_iop_buffer_dsc_t *dsc = &piece->pipe->dsc;
const gboolean wbon = dsc->temperature.enabled;
const dt_aligned_pixel_t icoeffs = { wbon ? dsc->temperature.coeffs[0] : 1.0f,
Expand Down Expand Up @@ -398,19 +397,19 @@ static float *_process_opposed(
}

#ifdef HAVE_OPENCL
static cl_int process_opposed_cl(
struct dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
cl_mem dev_in,
cl_mem dev_out,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out)
static cl_int process_opposed_cl(dt_iop_module_t *self,
dt_dev_pixelpipe_iop_t *piece,
cl_mem dev_in,
cl_mem dev_out,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out)
{
dt_iop_highlights_data_t *d = piece->data;
const dt_iop_highlights_global_data_t *gd = self->global_data;

const int devid = piece->pipe->devid;
const uint32_t filters = piece->pipe->dsc.filters;

const float clipval = highlights_clip_magics[DT_IOP_HIGHLIGHTS_OPPOSED] * d->clip;
const dt_iop_buffer_dsc_t *dsc = &piece->pipe->dsc;
const gboolean wbon = dsc->temperature.enabled;
Expand Down
15 changes: 8 additions & 7 deletions src/iop/hlreconstruct/segbased.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,14 +453,15 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
float *const output,
const dt_iop_roi_t *const roi_in,
const dt_iop_roi_t *const roi_out,
dt_iop_highlights_data_t *data,
dt_iop_highlights_data_t *d,
const int vmode,
float *tmpout)
{
const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])piece->pipe->dsc.xtrans;
const uint32_t filters = piece->pipe->dsc.filters;
const gboolean fullpipe = piece->pipe->type & DT_DEV_PIXELPIPE_FULL;
const float clipval = fmaxf(0.1f, 0.987f * data->clip);
const float clipval = MAX(0.1f, highlights_clip_magics[DT_IOP_HIGHLIGHTS_SEGMENTS] * d->clip);

const dt_aligned_pixel_t icoeffs = { piece->pipe->dsc.temperature.coeffs[0], piece->pipe->dsc.temperature.coeffs[1], piece->pipe->dsc.temperature.coeffs[2]};
const dt_aligned_pixel_t clips = { clipval * icoeffs[0], clipval * icoeffs[1], clipval * icoeffs[2]};
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)};
Expand All @@ -471,8 +472,8 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
late ? (float)(chr->D65coeffs[1] / chr->as_shot[1]) : 1.0f,
late ? (float)(chr->D65coeffs[2] / chr->as_shot[2]) : 1.0f,
1.0f };
const int recovery_mode = data->recovery;
const float strength = data->strength;
const int recovery_mode = d->recovery;
const float strength = d->strength;

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

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

if(dt_get_num_threads() >= HL_RGB_PLANES)
{
Expand All @@ -590,7 +591,7 @@ static void _process_segmentation(dt_dev_pixelpipe_iop_t *piece,
}

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

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

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

0 comments on commit ff2ef23

Please sign in to comment.