Skip to content

Commit fb31848

Browse files
Gamma corrected pipe input scaling
Non-raw images can have gamma corrected data as input possibly leading to bad results while we do the initial scaling. This scaling happens when generating the mipmap via `_init_f` and when inserting data into the pixelpipes. What we do now: - we test for the image having exif data as sRGB or AdobeRGB. - the colorin module does a much better check than exif data and possibly sets the profile type to any RGB mode. We keep track on this when setting the input profile and possibly change the image->colorspace (making sure to leave the exif checked data as they are). - dt_iop_clip_and_zoom() got an extra gboolean parameter `gamma`, if that is true the interpolation is encapsulated in rgb->linear and linear->rgb conversions. - we have a new kernel doing the flog gamma correction allowing OpenCL downscaling when inserting into the pipe. This results in improved data visualizing for all pipes and data taken from the preview pipe. qdefrve
1 parent 79c5fa8 commit fb31848

File tree

9 files changed

+140
-17
lines changed

9 files changed

+140
-17
lines changed

data/kernels/colorspaces.cl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,15 @@ colorspaces_transform_rgb_matrix_to_rgb(read_only image2d_t in, write_only image
8181

8282
write_imagef(out, (int2)(x, y), pixel);
8383
}
84+
85+
kernel void
86+
colorspaces_transform_gamma(read_only image2d_t in, write_only image2d_t out, const int width, const int height, const float gamma)
87+
{
88+
const int x = get_global_id(0);
89+
const int y = get_global_id(1);
90+
91+
if(x >= width || y >= height) return;
92+
93+
const float4 pixel = fmax(0.0f, read_imagef(in, sampleri, (int2)(x, y)) );
94+
write_imagef(out, (int2)(x, y), dtcl_pow(pixel, gamma));
95+
}

src/common/image.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ typedef enum dt_image_colorspace_t
107107
{
108108
DT_IMAGE_COLORSPACE_NONE,
109109
DT_IMAGE_COLORSPACE_SRGB,
110-
DT_IMAGE_COLORSPACE_ADOBE_RGB
110+
DT_IMAGE_COLORSPACE_ADOBE_RGB,
111+
DT_IMAGE_COLORSPACE_USER_RGB
111112
} dt_image_colorspace_t;
112113

113114
typedef struct dt_image_raw_parameters_t

src/common/iop_profile.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "common/debug.h"
2323
#include "common/imagebuf.h"
2424
#include "common/matrices.h"
25+
#include "common/image_cache.h"
2526
#include "control/control.h"
2627
#include "develop/imageop.h"
2728
#include "develop/imageop_math.h"
@@ -920,8 +921,25 @@ dt_ioppr_set_pipe_input_profile_info(struct dt_develop_t *dev,
920921
profile_info = dt_ioppr_add_profile_info_to_list(dev, DT_COLORSPACE_LIN_REC2020, "", intent);
921922
}
922923

923-
if(profile_info->type >= DT_COLORSPACE_EMBEDDED_ICC
924-
&& profile_info->type <= DT_COLORSPACE_ALTERNATE_MATRIX)
924+
const dt_imgid_t imgid = dev->image_storage.id;
925+
const dt_image_t *cimg = dt_image_cache_get(imgid, 'r');
926+
const dt_image_colorspace_t ocsp = cimg->colorspace;
927+
dt_image_cache_read_release(cimg);
928+
929+
const dt_colorspaces_color_profile_type_t ptype = profile_info->type;
930+
const gboolean exif_rgb = ocsp == DT_IMAGE_COLORSPACE_SRGB || ocsp == DT_IMAGE_COLORSPACE_ADOBE_RGB;
931+
const gboolean new_rgb = ptype == DT_COLORSPACE_SRGB || ptype == DT_COLORSPACE_ADOBERGB;
932+
const gboolean user_rgb = ocsp == DT_IMAGE_COLORSPACE_USER_RGB;
933+
if(!exif_rgb // don't fiddle with existing exif data
934+
&& ((new_rgb && !user_rgb) || (!new_rgb && user_rgb)))
935+
{
936+
dt_image_t *wimg = dt_image_cache_get(imgid, 'w');
937+
wimg->colorspace = new_rgb ? DT_IMAGE_COLORSPACE_USER_RGB : DT_IMAGE_COLORSPACE_NONE;
938+
dt_image_cache_write_release_info(wimg, DT_IMAGE_CACHE_RELAXED, NULL);
939+
}
940+
941+
if(ptype >= DT_COLORSPACE_EMBEDDED_ICC
942+
&& ptype <= DT_COLORSPACE_ALTERNATE_MATRIX)
925943
{
926944
/* We have a camera input matrix, these are not generated from files but in colorin,
927945
* so we need to fetch and replace them from somewhere.
@@ -1319,6 +1337,8 @@ dt_colorspaces_cl_global_t *dt_colorspaces_init_cl_global()
13191337
dt_opencl_create_kernel(program, "colorspaces_transform_rgb_matrix_to_lab");
13201338
g->kernel_colorspaces_transform_rgb_matrix_to_rgb =
13211339
dt_opencl_create_kernel(program, "colorspaces_transform_rgb_matrix_to_rgb");
1340+
g->kernel_colorspaces_gamma =
1341+
dt_opencl_create_kernel(program, "colorspaces_transform_gamma");
13221342
return g;
13231343
}
13241344

@@ -1330,6 +1350,7 @@ void dt_colorspaces_free_cl_global(dt_colorspaces_cl_global_t *g)
13301350
dt_opencl_free_kernel(g->kernel_colorspaces_transform_lab_to_rgb_matrix);
13311351
dt_opencl_free_kernel(g->kernel_colorspaces_transform_rgb_matrix_to_lab);
13321352
dt_opencl_free_kernel(g->kernel_colorspaces_transform_rgb_matrix_to_rgb);
1353+
dt_opencl_free_kernel(g->kernel_colorspaces_gamma);
13331354

13341355
free(g);
13351356
}

src/common/iop_profile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ typedef struct dt_colorspaces_cl_global_t
181181
int kernel_colorspaces_transform_lab_to_rgb_matrix;
182182
int kernel_colorspaces_transform_rgb_matrix_to_lab;
183183
int kernel_colorspaces_transform_rgb_matrix_to_rgb;
184+
int kernel_colorspaces_gamma;
184185
} dt_colorspaces_cl_global_t;
185186

186187
// must be in synch with colorspaces.cl dt_colorspaces_iccprofile_info_cl_t

src/common/mipmap_cache.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,10 +1379,12 @@ static void _init_f(dt_mipmap_buffer_t *mipmap_buf,
13791379
}
13801380
else
13811381
{
1382-
// downsample
1382+
// scale
1383+
const gboolean gamma = image->colorspace != DT_IMAGE_COLORSPACE_NONE;
13831384
dt_print_pipe(DT_DEBUG_PIPE,
1384-
"mipmap clip and zoom", NULL, NULL, DT_DEVICE_CPU, &roi_in, &roi_out);
1385-
dt_iop_clip_and_zoom(out, (const float *)buf.buf, &roi_out, &roi_in);
1385+
"mipmap clip&zoom", NULL, NULL, DT_DEVICE_NONE, &roi_in, &roi_out, "%s",
1386+
gamma ? "gamma corrected" : "");
1387+
dt_iop_clip_and_zoom(out, (const float *)buf.buf, &roi_out, &roi_in, gamma);
13861388
}
13871389

13881390
dt_mipmap_cache_release(&buf);

src/develop/imageop_math.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,35 @@ void dt_iop_clip_and_zoom_8(const uint8_t *i,
144144
}
145145
}
146146

147-
// apply clip and zoom on parts of a supplied full image.
148-
// roi_in and roi_out define which part to work on.
147+
/* apply clip and zoom on parts of a supplied full image, roi_in and roi_out define which part to work on.
148+
gamma correction around scaling supported.
149+
We don't do full RGB->linear and transformation but only use a gamma as that is fully sufficient
150+
for scaling.
151+
*/
149152
void dt_iop_clip_and_zoom(float *out,
150153
const float *const in,
151154
const dt_iop_roi_t *const roi_out,
152-
const dt_iop_roi_t *const roi_in)
155+
const dt_iop_roi_t *const roi_in,
156+
const gboolean gamma)
153157
{
154158
const dt_interpolation_t *itor = dt_interpolation_new(DT_INTERPOLATION_USERPREF);
155-
dt_interpolation_resample(itor, out, roi_out, in, roi_in);
159+
float *linear = gamma ? dt_alloc_align_float((size_t)roi_in->width * roi_in->height * 4) : NULL;
160+
if(!linear)
161+
return dt_interpolation_resample(itor, out, roi_out, in, roi_in);
162+
163+
static const dt_aligned_pixel_t two_point_four = { 2.4f, 2.4f, 2.4f, 2.4f };
164+
static const dt_aligned_pixel_t rev_two_point_four = { 1.0f / 2.4f, 1.0f / 2.4f, 1.0f / 2.4f, 1.0f / 2.4f };
165+
166+
DT_OMP_SIMD(aligned(in, linear : 16))
167+
for(size_t k = 0; k < (size_t)roi_in->width * roi_in->height*4; k += 4)
168+
dt_vector_powf(&in[k], two_point_four, &linear[k]);
169+
170+
dt_interpolation_resample(itor, out, roi_out, linear, roi_in);
171+
dt_free_align(linear);
172+
173+
DT_OMP_SIMD(aligned(out : 16))
174+
for(size_t k = 0; k < (size_t)roi_out->width * roi_out->height * 4; k += 4)
175+
dt_vector_powf(&out[k], rev_two_point_four, &out[k]);
156176
}
157177

158178
// apply clip and zoom on the image region supplied in the input buffer.

src/develop/imageop_math.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void dt_iop_flip_and_zoom_8(const uint8_t *in, int32_t iw, int32_t ih, uint8_t *
3434

3535
/** for homebrew pixel pipe: zoom pixel array. */
3636
void dt_iop_clip_and_zoom(float *out, const float *const in, const struct dt_iop_roi_t *const roi_out,
37-
const struct dt_iop_roi_t *const roi_in);
37+
const struct dt_iop_roi_t *const roi_in, const gboolean gamma);
3838

3939
/** zoom pixel array for roi buffers. */
4040
void dt_iop_clip_and_zoom_roi(float *out, const float *const in, const struct dt_iop_roi_t *const roi_out,

src/develop/pixelpipe_hb.c

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,15 +1724,81 @@ static gboolean _dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe,
17241724
roi_in.height = pipe->iheight;
17251725
roi_in.scale = 1.0f;
17261726
const gboolean valid_bpp = (bpp == 4 * sizeof(float));
1727-
1727+
const gboolean gamma = dev->image_storage.colorspace != DT_IMAGE_COLORSPACE_NONE;
1728+
#ifdef HAVE_OPENCL
1729+
const size_t in_size = bpp * roi_in.width * roi_in.height;
1730+
const gboolean cl_scale_possible = ((2 * in_size) < dt_opencl_get_device_available(pipe->devid))
1731+
&& roi_out->width < roi_in.width
1732+
&& roi_out->height < roi_in.height;
1733+
#else
1734+
const gboolean cl_scale_possible = FALSE:
1735+
#endif
17281736
dt_print_pipe(DT_DEBUG_PIPE,
17291737
"pipe data: clip&zoom",
1730-
pipe, module, DT_DEVICE_CPU, &roi_in, roi_out, "%s%s",
1731-
valid_bpp ? "" : "requires 4 floats data",
1732-
aligned_input ? "" : "non-aligned input buffer");
1738+
pipe, module, pipe->devid, &roi_in, roi_out, "%s%s%s%s",
1739+
valid_bpp ? "" : "requires 4 floats data ",
1740+
aligned_input ? "" : "non-aligned input buffer ",
1741+
cl_scale_possible ? "OpenCL scaling " : "",
1742+
gamma ? "gamma corrected" : "");
17331743

17341744
if(valid_bpp && aligned_input)
1735-
dt_iop_clip_and_zoom(*output, pipe->input, roi_out, &roi_in);
1745+
{
1746+
gboolean done = FALSE;
1747+
#ifdef HAVE_OPENCL
1748+
if(cl_scale_possible)
1749+
{
1750+
cl_mem tmp_input = dt_opencl_alloc_device(pipe->devid, roi_in.width, roi_in.height, bpp);
1751+
cl_mem linear_input = dt_opencl_alloc_device(pipe->devid, roi_in.width, roi_in.height, bpp);
1752+
cl_mem tmp_output = NULL;
1753+
cl_mem linear_output = NULL;
1754+
if(tmp_input && linear_input)
1755+
{
1756+
cl_int err = dt_opencl_write_host_to_device(pipe->devid, pipe->input, tmp_input, roi_in.width, roi_in.height, bpp);
1757+
if(err == CL_SUCCESS)
1758+
{
1759+
const float fgamma = 2.4f;
1760+
err = dt_opencl_enqueue_kernel_2d_args(pipe->devid, darktable.opencl->colorspaces->kernel_colorspaces_gamma, roi_in.width, roi_in.height,
1761+
CLARG(tmp_input), CLARG(linear_input), CLARG(roi_in.width), CLARG(roi_in.height), CLARG(fgamma));
1762+
dt_opencl_release_mem_object(tmp_input);
1763+
tmp_input = NULL;
1764+
}
1765+
if(err == CL_SUCCESS)
1766+
{
1767+
linear_output = dt_opencl_alloc_device(pipe->devid, roi_out->width, roi_out->height, bpp);
1768+
if(!linear_output) err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1769+
if(err == CL_SUCCESS)
1770+
err = dt_iop_clip_and_zoom_cl(pipe->devid, linear_output, linear_input, roi_out, &roi_in);
1771+
dt_opencl_release_mem_object(linear_input);
1772+
linear_input = NULL;
1773+
}
1774+
if(err == CL_SUCCESS)
1775+
{
1776+
tmp_output = dt_opencl_alloc_device(pipe->devid, roi_out->width, roi_out->height, bpp);
1777+
if(!tmp_output) err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1778+
const float fgamma = 1.0f / 2.4f;
1779+
if(err == CL_SUCCESS)
1780+
err = dt_opencl_enqueue_kernel_2d_args(pipe->devid, darktable.opencl->colorspaces->kernel_colorspaces_gamma, roi_out->width, roi_out->height,
1781+
CLARG(linear_output), CLARG(tmp_output), CLARG(roi_out->width), CLARG(roi_out->height), CLARG(fgamma));
1782+
}
1783+
if(err == CL_SUCCESS)
1784+
err = dt_opencl_copy_device_to_host(pipe->devid, *output, tmp_output, roi_out->width, roi_out->height, bpp);
1785+
if(err == CL_SUCCESS)
1786+
done = TRUE;
1787+
else
1788+
dt_print(DT_DEBUG_PIPE | DT_DEBUG_OPENCL, "OpenCL pipe data: clip&zoom failed");
1789+
}
1790+
dt_opencl_release_mem_object(tmp_input);
1791+
dt_opencl_release_mem_object(tmp_output);
1792+
dt_opencl_release_mem_object(linear_input);
1793+
dt_opencl_release_mem_object(linear_output);
1794+
}
1795+
1796+
if(!done)
1797+
dt_iop_clip_and_zoom(*output, pipe->input, roi_out, &roi_in, gamma);
1798+
#else
1799+
dt_iop_clip_and_zoom(*output, pipe->input, roi_out, &roi_in, gamma);
1800+
#endif
1801+
}
17361802
else
17371803
{
17381804
memset(*output, 0, (size_t)roi_out->width * roi_out->height * bpp);

src/iop/finalscale.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ void process(dt_iop_module_t *self,
186186
if(exporting)
187187
dt_iop_clip_and_zoom_roi((float *)ovoid, (float *)ivoid, roi_out, roi_in);
188188
else
189-
dt_iop_clip_and_zoom((float *)ovoid, (float *)ivoid, roi_out, roi_in);
189+
dt_iop_clip_and_zoom((float *)ovoid, (float *)ivoid, roi_out, roi_in, FALSE);
190190
}
191191

192192
void commit_params(dt_iop_module_t *self,

0 commit comments

Comments
 (0)