8000 Introduce distort-aware pickers by jenshannoschwalm · Pull Request #16534 · darktable-org/darktable · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Introduce distort-aware pickers #16534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 177 additions & 1 deletion src/common/color_picker.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
This file is part of darktable,
Copyright (C) 2016-2023 darktable developers.
Copyright (C) 2016-2024 darktable developers.

darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -271,6 +271,175 @@ static void _color_picker_work_1ch(const float *const pixel,
pick[DT_PICK_MEAN][c] = weights[c] ? (acc[c] / (float)weights[c]) : 0.0f;
}

void dt_color_picker_backtransform_box(dt_develop_t *dev,
const int num,
const float *in,
float *out)
{
const float wd = dev->preview_pipe->iwidth;
const float ht = dev->preview_pipe->iheight;
const float wdp = dev->preview_pipe->processed_width;
const float htp = dev->preview_pipe->processed_height;
const gboolean box = num == 2;
if(wd < 1.0f || ht < 1.0f || wdp < 1.0f || htp < 1.0f)
{
for(int i = 0; i < num; i++)
out[i] = in[i];
return;
}

dt_boundingbox_t fbox = { wdp * in[0],
htp * in[1],
box ? wdp * in[2] : 0.0f,
box ? htp * in[3] : 0.0f };
dt_dev_distort_backtransform(dev, fbox, num);

out[0] = fbox[0] / wd;
out[1] = fbox[1] / ht;
if(box)
{
out[2] = fbox[2] / wd;
out[3] = fbox[3] / ht;
}
}

void dt_color_picker_transform_box(dt_develop_t *dev,
const int num,
const float *in,
float *out)
{
const float wd = dev->preview_pipe->iwidth;
const float ht = dev->preview_pipe->iheight;
const gboolean box = num == 2;
if(wd < 1.0f || ht < 1.0f)
{
for(int i = 0; i < num; i++)
out[i] = in[i];
return;
}

const float x0 = wd * in[0];
const float y0 = ht * in[1];
const float x1 = wd * in[2];
const float y1 = ht * in[3];

float fbox[8] = { x0,y0, x0,y1, x1,y0, x1,y1 };
dt_dev_distort_transform(dev, fbox, box ? 4 : 1);

if(box) // sort the 4 point coordinates
{
#define SWAP(a, b) { const float tmp = (a); (a) = (b); (b) = tmp; }
if(fbox[0] > fbox[2]) SWAP(fbox[0], fbox[2]);
if(fbox[1] > fbox[3]) SWAP(fbox[1], fbox[3]);
if(fbox[4] > fbox[6]) SWAP(fbox[4], fbox[6]);
if(fbox[5] > fbox[7]) SWAP(fbox[5], fbox[7]);
if(fbox[0] > fbox[4]) SWAP(fbox[0], fbox[4]);
if(fbox[1] > fbox[5]) SWAP(fbox[1], fbox[5]);
if(fbox[2] > fbox[6]) SWAP(fbox[2], fbox[6]);
if(fbox[3] > fbox[7]) SWAP(fbox[3], fbox[7]);
if(fbox[2] > fbox[4]) SWAP(fbox[2], fbox[4]);
if(fbox[3] > fbox[5]) SWAP(fbox[3], fbox[5]);
#undef SWAP

out[0] = 0.5f * (fbox[0] + fbox[2]);
out[1] = 0.5f * (fbox[1] + fbox[3]);
out[2] = 0.5f * (fbox[4] + fbox[6]);
out[3] = 0.5f * (fbox[5] + fbox[7]);
}
else
{
out[0] = fbox[0];
out[1] = fbox[1];
}
}

// calculate box in current module's coordinates for the color picker
gboolean dt_color_picker_box(dt_iop_module_t *module,
const dt_iop_roi_t *roi,
const dt_colorpicker_sample_t *const sample,
dt_pixelpipe_picker_source_t picker_source,
int *box)
{
if(picker_source == PIXELPIPE_PICKER_OUTPUT
&& !sample->pick_output)
return TRUE;

dt_develop_t *dev = darktable.develop;
const float wd = dev->preview_pipe->iwidth;
const float ht = dev->preview_pipe->iheight;

const int width = roi->width;
const int height = roi->height;
const gboolean isbox = sample->size == DT_LIB_COLORPICKER_SIZE_BOX;

const float bx0 = wd * sample->box[0];
const float by0 = ht * sample->box[1];
const float bx1 = wd * sample->box[2];
const float by1 = ht * sample->box[3];

const float sx = sample->point[0] * wd;
const float sy = sample->point[1] * ht;

/* get absolute pixel coordinates in final preview image.
we transform back all 4 corner locations to current module coordinates,
sort the coordinates, and use average of 2 highest and 2 lowest for the
resulting rectangle.
*/
float fbox[8] = { isbox ? bx0 : sx, isbox ? by0 : sy,
isbox ? bx0 : sx, isbox ? by1 : sy,
isbox ? bx1 : sx, isbox ? by0 : sy,
isbox ? bx1 : sx, isbox ? by1 : sy };

dt_dev_distort_transform_plus
(dev, dev->preview_pipe, module->iop_order,
((picker_source == PIXELPIPE_PICKER_INPUT) ? DT_DEV_TRANSFORM_DIR_BACK_INCL : DT_DEV_TRANSFORM_DIR_BACK_EXCL),
fbox, 4);

#define SWAP(a, b) { const float tmp = (a); (a) = (b); (b) = tmp; }
if(fbox[0] > fbox[2]) SWAP(fbox[0], fbox[2]);
if(fbox[1] > fbox[3]) SWAP(fbox[1], fbox[3]);
if(fbox[4] > fbox[6]) SWAP(fbox[4], fbox[6]);
if(fbox[5] > fbox[7]) SWAP(fbox[5], fbox[7]);
if(fbox[0] > fbox[4]) SWAP(fbox[0], fbox[4]);
if(fbox[1] > fbox[5]) SWAP(fbox[1], fbox[5]);
if(fbox[2] > fbox[6]) SWAP(fbox[2], fbox[6]);
if(fbox[3] > fbox[7]) SWAP(fbox[3], fbox[7]);
if(fbox[2] > fbox[4]) SWAP(fbox[2], fbox[4]);
if(fbox[3] > fbox[5]) SWAP(fbox[3], fbox[5]);
#undef SWAP

box[0] = 0.5f * (fbox[0] + fbox[2]) - roi->x;
box[1] = 0.5f * (fbox[1] + fbox[3]) - roi->y;
box[2] = 0.5f * (fbox[4] + fbox[6]) - roi->x;
box[3] = 0.5f * (fbox[5] + fbox[7]) - roi->y;

// make sure we sample at least one point
box[2] = MAX(box[2], box[0] + 1);
box[3] = MAX(box[3], box[1] + 1);

// do not continue if box is completely outside of roi
// FIXME: on invalid box, caller should set sample to something like
// NaN to flag it as invalid
if( box[0] >= width
|| box[1] >= height
|| box[2] < 0
|| box[3] < 0)
return TRUE;

// clamp bounding box to roi
box[0] = CLAMP(box[0], 0, width - 1);
box[1] = CLAMP(box[1], 0, height - 1);
box[2] = CLAMP(box[2], 1, width);
box[3] = CLAMP(box[3], 1, height);

// safety check: area needs to have minimum 1 pixel width and height
if( box[2] - box[0] < 1
|| box[3] - box[1] < 1)
return TRUE;

return FALSE;
}

void dt_color_picker_helper(const dt_iop_buffer_dsc_t *dsc,
const float *const pixel,
const dt_iop_roi_t *roi,
Expand All @@ -284,6 +453,13 @@ void dt_color_picker_helper(const dt_iop_buffer_dsc_t *dsc,
dt_times_t start_time = { 0 };
dt_get_perf_times(&start_time);

for_four_channels(k)
{
pick[DT_PICK_MEAN][k] = 0.0f;
pick[DT_PICK_MIN][k] = FLT_MAX;
pick[DT_PICK_MAX][k] = -FLT_MAX;
}

if(dsc->channels == 4u)
{
float *restrict denoised = NULL;
Expand Down
21 changes: 20 additions & 1 deletion src/common/color_picker.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
This file is part of darktable,
Copyright (C) 2016-2020 darktable developers.
Copyright (C) 2016-2024 darktable developers.

darktable is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand All @@ -25,6 +25,25 @@ struct dt_iop_buffer_dsc_t;
struct dt_iop_roi_t;
enum dt_iop_colorspace_type_t;

typedef enum dt_pixelpipe_picker_source_t
{
PIXELPIPE_PICKER_INPUT = 0,
PIXELPIPE_PICKER_OUTPUT = 1
} dt_pixelpipe_picker_source_t;

void dt_color_picker_backtransform_box(dt_develop_t *dev,
const int num,
const float *in,
float *out);
void dt_color_picker_transform_box(dt_develop_t *dev,
const int num,
const float *in,
float *out);
gboolean dt_color_picker_box(dt_iop_module_t *module,
const dt_iop_roi_t *roi,
const dt_colorpicker_sample_t *const sample,
dt_pixelpipe_picker_source_t picker_source,
int *box);
void dt_color_picker_helper(const struct dt_iop_buffer_dsc_t *dsc, const float *const pixel,
const struct dt_iop_roi_t *roi, const int *const box,
const gboolean denoise,
Expand Down
113 changes: 11 additions & 102 deletions src/develop/pixelpipe_hb.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ typedef enum dt_pixelpipe_flow_t
PIXELPIPE_FLOW_BLENDED_ON_GPU = 1 << 7
} dt_pixelpipe_flow_t;

typedef enum dt_pixelpipe_picker_source_t
{
PIXELPIPE_PICKER_INPUT = 0,
PIXELPIPE_PICKER_OUTPUT = 1
} dt_pixelpipe_picker_source_t;

#include "develop/pixelpipe_cache.c"

const char *dt_dev_pixelpipe_type_to_str(const int pipe_type)
Expand Down Expand Up @@ -782,83 +776,6 @@ static void _histogram_collect_cl(const int devid,
}
#endif

// calculate box in current module's coordinates for the color picker
// FIXME: move this to common color picker code?
static gboolean _pixelpipe_picker_box(dt_iop_module_t *module,
const dt_iop_roi_t *roi,
const dt_colorpicker_sample_t *const sample,
dt_pixelpipe_picker_source_t picker_source,
int *box)
{
if(picker_source == PIXELPIPE_PICKER_OUTPUT
&& !sample->pick_output)
return TRUE;

float wd, ht;
dt_dev_get_preview_size(darktable.develop, &wd, &ht);
const int width = roi->width;
const int height = roi->height;
dt_boundingbox_t fbox = { 0.0f };

// get absolute pixel coordinates in final preview image
if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX)
{
for(int k = 0; k < 4; k += 2)
fbox[k] = sample->box[k] * wd;
for(int k = 1; k < 4; k += 2)
fbox[k] = sample->box[k] * ht;
}
else if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT)
{
fbox[0] = fbox[2] = sample->point[0] * wd;
fbox[1] = fbox[3] = sample->point[1] * ht;
}

// transform back to current module coordinates
dt_dev_distort_backtransform_plus
(darktable.develop, darktable.develop->preview_pipe, module->iop_order,
((picker_source == PIXELPIPE_PICKER_INPUT)
? DT_DEV_TRANSFORM_DIR_FORW_INCL
: DT_DEV_TRANSFORM_DIR_FORW_EXCL),
fbox, 2);

fbox[0] -= roi->x;
fbox[1] -= roi->y;
fbox[2] -= roi->x;
fbox[3] -= roi->y;

// re-order edges of bounding box
box[0] = fminf(fbox[0], fbox[2]);
box[1] = fminf(fbox[1], fbox[3]);
box[2] = fmaxf(fbox[0], fbox[2]);
box[3] = fmaxf(fbox[1], fbox[3]);

// make sure we sample at least one point
box[2] = fmaxf(box[2], box[0] + 1);
box[3] = fmaxf(box[3], box[1] + 1);

// do not continue if box is completely outside of roi
// FIXME: on invalid box, caller should set sample to something like
// NaN to flag it as invalid
if(box[0] >= width
|| box[1] >= height
|| box[2] < 0
|| box[3] < 0)
return 1;

// clamp bounding box to roi
box[0] = CLAMP(box[0], 0, width - 1);
box[1] = CLAMP(box[1], 0, height - 1);
box[2] = CLAMP(box[2], 1, width);
box[3] = CLAMP(box[3], 1, height);

// safety check: area needs to have minimum 1 pixel width and height
if(box[2] - box[0] < 1
|| box[3] - box[1] < 1)
return TRUE;

return FALSE;
}

// color picking for module
// FIXME: make called with: lib_colorpicker_sample_statistics pick
Expand All @@ -874,16 +791,12 @@ static void _pixelpipe_picker(dt_iop_module_t *module,
const dt_pixelpipe_picker_source_t picker_source)
{
int box[4] = { 0 };
lib_colorpicker_stats pick;

// FIXME: don't need to initialize this if dt_color_picker_helper() does
lib_colorpicker_stats pick =
{ { 0.0f, 0.0f, 0. FFB0 0f, 0.0f },
{ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX },
{ -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX } };

if(!_pixelpipe_picker_box(module, roi,
darktable.lib->proxy.colorpicker.primary_sample,
picker_source, box))
const gboolean nobox = dt_color_picker_box(module, roi,
darktable.lib->proxy.colorpicker.primary_sample,
picker_source, box);
if(!nobox)
{
const dt_iop_order_iccprofile_info_t *const profile =
dt_ioppr_get_pipe_current_profile_info(module, piece->pipe);
Expand All @@ -895,9 +808,9 @@ static void _pixelpipe_picker(dt_iop_module_t *module,

for_four_channels(k)
{
picked_color_min[k] = pick[DT_PICK_MIN][k];
picked_color_max[k] = pick[DT_PICK_MAX][k];
picked_color[k] = pick[DT_PICK_MEAN][k];
picked_color_min[k] = nobox ? FLT_MAX : pick[DT_PICK_MIN][k];
picked_color_max[k] = nobox ? -FLT_MAX : pick[DT_PICK_MAX][k];
picked_color[k] = nobox ? 0.0f : pick[DT_PICK_MEAN][k];
}
}

Expand Down Expand Up @@ -927,7 +840,7 @@ static void _pixelpipe_picker_cl(const int devid,
{
int box[4] = { 0 };

if(_pixelpipe_picker_box(module, roi,
if(dt_color_picker_box(module, roi,
darktable.lib->proxy.colorpicker.primary_sample,
picker_source, box))
{
Expand Down Expand Up @@ -977,11 +890,7 @@ static void _pixelpipe_picker_cl(const int devid,
box[2] = region[0];
box[3] = region[1];

// FIXME: don't need to initialize this if dt_color_picker_helper() does
lib_colorpicker_stats pick =
{ { 0.0f, 0.0f, 0.0f, 0.0f },
{ FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX },
{ -FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX } };
lib_colorpicker_stats pick;

const dt_iop_order_iccprofile_info_t *const profile =
dt_ioppr_get_pipe_current_profile_info(module, piece->pipe);
Expand Down Expand Up @@ -1033,7 +942,7 @@ static void _pixelpipe_pick_samples(dt_develop_t *dev,
int box[4];
dt_colorpicker_sample_t *sample = samples->data;
if(!sample->locked &&
!_pixelpipe_picker_box(module, roi_in, sample, PIXELPIPE_PICKER_INPUT, box))
!dt_color_picker_box(module, roi_in, sample, PIXELPIPE_PICKER_INPUT, box))
{
// pixel input is in display profile, hence the sample output will be as well
dt_color_picker_helper(dsc, input, roi_in, box, sample->denoise,
Expand Down
Loading
0