Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
color_picker.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016 Roman Lebedev.
4 Copyright (C) 2019 Andreas Schneider.
5 Copyright (C) 2019 Edgardo Hoszowski.
6 Copyright (C) 2019-2021 Pascal Obry.
7 Copyright (C) 2020-2021 Harold le Clément de Saint-Marcq.
8 Copyright (C) 2020 Heiko Bauke.
9 Copyright (C) 2020 Hubert Kowalski.
10 Copyright (C) 2020-2021 Ralf Brown.
11 Copyright (C) 2021 Hanno Schwalm.
12 Copyright (C) 2022, 2025-2026 Aurélien PIERRE.
13 Copyright (C) 2022 Martin Bařinka.
14 Copyright (C) 2022 Philipp Lutz.
15
16 darktable is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 darktable is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with darktable. If not, see <http://www.gnu.org/licenses/>.
28*/
29
30#include "common/color_picker.h"
31#include "common/bspline.h"
32#include "common/darktable.h"
34#include "common/iop_profile.h"
35#include "develop/format.h"
36#include "develop/imageop.h"
38
39static inline size_t _box_size(const int *const box)
40{
41 return (size_t)((box[3] - box[1]) * (box[2] - box[0]));
42}
43
44static inline void rgb_to_JzCzhz(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t JzCzhz,
45 const dt_iop_order_iccprofile_info_t *const profile);
46
57static void _color_picker_convert_buffer(const float *const restrict input, float *const restrict output,
58 const size_t pixels, const dt_iop_colorspace_type_t image_cst,
59 const dt_iop_colorspace_type_t picker_cst,
60 const dt_iop_order_iccprofile_info_t *const profile)
61{
62 if(image_cst == IOP_CS_LAB && picker_cst == IOP_CS_LCH)
63 {
65 for(size_t k = 0; k < pixels; k++)
66 {
67 const size_t offset = 4 * k;
68 dt_Lab_2_LCH(input + offset, output + offset);
69 output[offset + 3] = input[offset + 3];
70 }
71 }
72 else if(dt_iop_colorspace_is_rgb(image_cst) && picker_cst == IOP_CS_HSL)
73 {
75 for(size_t k = 0; k < pixels; k++)
76 {
77 const size_t offset = 4 * k;
78 dt_RGB_2_HSL(input + offset, output + offset);
79 output[offset + 3] = input[offset + 3];
80 }
81 }
82 else if(image_cst == IOP_CS_LAB && picker_cst == IOP_CS_RGB)
83 {
85 for(size_t k = 0; k < pixels; k++)
86 {
87 const size_t offset = 4 * k;
88 dt_ioppr_lab_to_rgb_matrix(input + offset, output + offset, profile->matrix_out_transposed,
89 profile->lut_out, profile->unbounded_coeffs_out,
90 profile->lutsize, profile->nonlinearlut);
91 output[offset + 3] = input[offset + 3];
92 }
93 }
94 else if(dt_iop_colorspace_is_rgb(image_cst) && picker_cst == IOP_CS_LAB)
95 {
97 for(size_t k = 0; k < pixels; k++)
98 {
99 const size_t offset = 4 * k;
100 dt_ioppr_rgb_matrix_to_lab(input + offset, output + offset, profile->matrix_in_transposed,
101 profile->lut_in, profile->unbounded_coeffs_in,
102 profile->lutsize, profile->nonlinearlut);
103 output[offset + 3] = input[offset + 3];
104 }
105 }
106 else if(dt_iop_colorspace_is_rgb(image_cst) && picker_cst == IOP_CS_JZCZHZ)
107 {
109 for(size_t k = 0; k < pixels; k++)
110 {
111 const size_t offset = 4 * k;
112 rgb_to_JzCzhz(input + offset, output + offset, profile);
113 output[offset + 3] = input[offset + 3];
114 }
115 }
116}
117
118__OMP_DECLARE_SIMD__(aligned(rgb, JzCzhz: 16) uniform(profile))
120 const dt_iop_order_iccprofile_info_t *const profile)
121{
122 dt_aligned_pixel_t XYZ_D65 = { 0.0f, 0.0f, 0.0f };
123 dt_aligned_pixel_t JzAzBz = { 0.0f, 0.0f, 0.0f };
124
125 if(profile)
126 {
127 dt_aligned_pixel_t XYZ_D50 = { 0.0f, 0.0f, 0.0f };
128 dt_ioppr_rgb_matrix_to_xyz(rgb, XYZ_D50, profile->matrix_in_transposed, profile->lut_in, profile->unbounded_coeffs_in,
129 profile->lutsize, profile->nonlinearlut);
130 dt_XYZ_D50_2_XYZ_D65(XYZ_D50, XYZ_D65);
131 }
132 else
133 {
134 // This should not happen (we don't know what RGB is), but use this when profile is not defined
135 dt_XYZ_D50_2_XYZ_D65(rgb, XYZ_D65);
136 }
137
138 dt_XYZ_2_JzAzBz(XYZ_D65, JzAzBz);
139 dt_JzAzBz_2_JzCzhz(JzAzBz, JzCzhz);
140}
142 const float *const pixels, const float w, const size_t width)
143{
144 for(size_t i = 0; i < width; i += 4)
145 {
146 dt_aligned_pixel_t pick = { pixels[i], pixels[i + 1], pixels[i + 2], 0.0f };
147 for(size_t k = 0; k < 4; k++)
148 {
149 avg[k] += w * pick[k];
150 min[k] = fminf(min[k], pick[k]);
151 max[k] = fmaxf(max[k], pick[k]);
152 }
153 }
154}
156 const float *const pixels, const float w, const size_t width)
157{
158 for(size_t i = 0; i < width; i += 4)
159 {
160 dt_aligned_pixel_t pick = { pixels[i], pixels[i + 1], pixels[i + 2], 0.0f };
161 pick[3] = pick[0] < 0.5f ? pick[0] + 0.5f : pick[0] - 0.5f;
162 for(size_t k = 0; k < 4; k++)
163 {
164 avg[k] += w * pick[k];
165 min[k] = fminf(min[k], pick[k]);
166 max[k] = fmaxf(max[k], pick[k]);
167 }
168 }
169}
171 dt_aligned_pixel_t max, const float *const pixels,
172 const float w, const size_t width)
173{
174 for(size_t i = 0; i < width; i += 4)
175 {
176 dt_aligned_pixel_t pick = { pixels[i], pixels[i + 1], pixels[i + 2], 0.0f };
177 pick[3] = pick[2] < 0.5f ? pick[2] + 0.5f : pick[2] - 0.5f;
178 for(size_t k = 0; k < 4; k++)
179 {
180 avg[k] += w * pick[k];
181 min[k] = fminf(min[k], pick[k]);
182 max[k] = fmaxf(max[k], pick[k]);
183 }
184 }
185}
187 const float *const pixels, const float w, const size_t width)
188{
189 for(size_t i = 0; i < width; i += 4)
190 {
192 dt_Lab_2_LCH(pixels + i, pick);
193 pick[3] = pick[2] < 0.5f ? pick[2] + 0.5f : pick[2] - 0.5f;
194 for(size_t k = 0; k < 4; k++)
195 {
196 avg[k] += w * pick[k];
197 min[k] = fminf(min[k], pick[k]);
198 max[k] = fmaxf(max[k], pick[k]);
199 }
200 }
201}
203 const float *const pixels, const float w, const size_t width)
204{
205 for(size_t i = 0; i < width; i += 4)
206 {
208 dt_RGB_2_HSL(pixels + i, pick);
209 pick[3] = pick[0] < 0.5f ? pick[0] + 0.5f : pick[0] - 0.5f;
210 for(size_t k = 0; k < 4; k++)
211 {
212 avg[k] += w * pick[k];
213 min[k] = fminf(min[k], pick[k]);
214 max[k] = fmaxf(max[k], pick[k]);
215 }
216 }
217}
219 const float *const pixels, const float w, const size_t width,
220 const dt_iop_order_iccprofile_info_t *const profile)
221{
222 for(size_t i = 0; i < width; i += 4)
223 {
225 rgb_to_JzCzhz(pixels + i, pick, profile);
226 pick[3] = pick[2] < 0.5f ? pick[2] + 0.5f : pick[2] - 0.5f;
227 for(size_t k = 0; k < 4; k++)
228 {
229 avg[k] += w * pick[k];
230 min[k] = fminf(min[k], pick[k]);
231 max[k] = fmaxf(max[k], pick[k]);
232 }
233 }
234}
235
236static void color_picker_helper_4ch_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
237 const dt_iop_roi_t *const roi, const int *const box,
238 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
239 dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to,
240 const dt_iop_order_iccprofile_info_t *const profile)
241{
242 const int width = roi->width;
243
244 const size_t size = _box_size(box);
245 const size_t stride = 4 * (size_t)(box[2] - box[0]);
246 const size_t off_mul = 4 * width;
247 const size_t off_add = 4 * box[0];
248
249 const float w = 1.0f / (float)size;
250
251 // code path for small region, especially for color picker point mode
252 if(cst_to == IOP_CS_LCH)
253 {
254 for(size_t j = box[1]; j < box[3]; j++)
255 {
256 const size_t offset = j * off_mul + off_add;
257 _color_picker_lch(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
258 }
259 }
260 else if(cst_to == IOP_CS_HSL)
261 {
262 for(size_t j = box[1]; j < box[3]; j++)
263 {
264 const size_t offset = j * off_mul + off_add;
265 _color_picker_hsl(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
266 }
267 }
268 else if(cst_to == IOP_CS_JZCZHZ)
269 {
270 for(size_t j = box[1]; j < box[3]; j++)
271 {
272 const size_t offset = j * off_mul + off_add;
273 _color_picker_jzczhz(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride, profile);
274 }
275 }
276 else
277 {
278 for(size_t j = box[1]; j < box[3]; j++)
279 {
280 const size_t offset = j * off_mul + off_add;
281 _color_picker_rgb_or_lab(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
282 }
283 }
284}
285
286static void color_picker_helper_4ch_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
287 const dt_iop_roi_t *const roi, const int *const box,
288 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
289 dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to,
290 const dt_iop_order_iccprofile_info_t *const profile)
291{
292 const int width = roi->width;
293
294 const size_t size = _box_size(box);
295 const size_t stride = 4 * (size_t)(box[2] - box[0]);
296 const size_t off_mul = 4 * width;
297 const size_t off_add = 4 * box[0];
298
299 const float w = 1.0f / (float)size;
300
301 const size_t numthreads = MAX(1, (size_t)omp_get_max_threads());
302
303 size_t allocsize;
304 float *const restrict mean = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
305 float *const restrict mmin = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
306 float *const restrict mmax = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
307
308 if(IS_NULL_PTR(mean) || IS_NULL_PTR(mmax) || IS_NULL_PTR(mmin))
309 goto error;
310
311 for(int n = 0; n < allocsize * numthreads; n++)
312 {
313 mean[n] = 0.0f;
314 mmin[n] = INFINITY;
315 mmax[n] = -INFINITY;
316 }
317
318 if(cst_to == IOP_CS_LCH)
319 {
321 {
322 float *const restrict tmean = dt_get_perthread(mean,allocsize);
323 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
324 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
326 for(size_t j = box[1]; j < box[3]; j++)
327 {
328 const size_t offset = j * off_mul + off_add;
329 _color_picker_lch(tmean, tmmin, tmmax, pixel + offset, w, stride);
330 }
331 }
332 }
333 else if(cst_to == IOP_CS_HSL)
334 {
336 {
337 float *const restrict tmean = dt_get_perthread(mean,allocsize);
338 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
339 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
341 for(size_t j = box[1]; j < box[3]; j++)
342 {
343 const size_t offset = j * off_mul + off_add;
344 _color_picker_hsl(tmean, tmmin, tmmax, pixel + offset, w, stride);
345 }
346 }
347 }
348 else if(cst_to == IOP_CS_JZCZHZ)
349 {
351 {
352 float *const restrict tmean = dt_get_perthread(mean,allocsize);
353 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
354 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
356 for(size_t j = box[1]; j < box[3]; j++)
357 {
358 const size_t offset = j * off_mul + off_add;
359 _color_picker_jzczhz(tmean, tmmin, tmmax, pixel + offset, w, stride, profile);
360 }
361 }
362 }
363 else
364 {
366 {
367 float *const restrict tmean = dt_get_perthread(mean,allocsize);
368 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
369 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
371 for(size_t j = box[1]; j < box[3]; j++)
372 {
373 const size_t offset = j * off_mul + off_add;
374 _color_picker_rgb_or_lab(tmean, tmmin, tmmax, pixel + offset, w, stride);
375 }
376 }
377 }
378
379 for(int n = 0; n < numthreads; n++)
380 {
381 for(int k = 0; k < 4; k++)
382 {
383 picked_color[k] += mean[allocsize * n + k];
384 picked_color_min[k] = fminf(picked_color_min[k], mmin[allocsize * n + k]);
385 picked_color_max[k] = fmaxf(picked_color_max[k], mmax[allocsize * n + k]);
386 }
387 }
388
389error:;
393}
394
395static void color_picker_helper_4ch(const dt_iop_buffer_dsc_t *dsc, const float *const pixel,
396 const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color,
397 dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max,
398 const dt_iop_colorspace_type_t cst_to,
399 const dt_iop_order_iccprofile_info_t *const profile)
400{
401 const size_t size = _box_size(box);
402
403 if(size > 100) // avoid inefficient multi-threading in case of small region size (arbitrary limit)
404 return color_picker_helper_4ch_parallel(dsc, pixel, roi, box, picked_color, picked_color_min,
405 picked_color_max, cst_to, profile);
406 else
407 return color_picker_helper_4ch_seq(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max,
408 cst_to, profile);
409}
410
411static void color_picker_helper_4ch_converted_seq(const float *const pixel, const dt_iop_roi_t *const roi,
412 const int *const box, dt_aligned_pixel_t picked_color,
413 dt_aligned_pixel_t picked_color_min,
414 dt_aligned_pixel_t picked_color_max,
415 const dt_iop_colorspace_type_t picker_cst)
416{
417 const int width = roi->width;
418 const size_t size = _box_size(box);
419 const size_t stride = 4 * (size_t)(box[2] - box[0]);
420 const size_t off_mul = 4 * width;
421 const size_t off_add = 4 * box[0];
422 const float w = 1.0f / (float)size;
423
424 for(size_t j = box[1]; j < box[3]; j++)
425 {
426 const size_t offset = j * off_mul + off_add;
427 if(picker_cst == IOP_CS_HSL)
428 _color_picker_direct_hsl(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
429 else if(picker_cst == IOP_CS_LCH || picker_cst == IOP_CS_JZCZHZ)
430 _color_picker_direct_lch_or_jzczhz(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
431 else
432 _color_picker_rgb_or_lab(picked_color, picked_color_min, picked_color_max, pixel + offset, w, stride);
433 }
434}
435
436static void color_picker_helper_4ch_converted_parallel(const float *const pixel, const dt_iop_roi_t *const roi,
437 const int *const box, dt_aligned_pixel_t picked_color,
438 dt_aligned_pixel_t picked_color_min,
439 dt_aligned_pixel_t picked_color_max,
440 const dt_iop_colorspace_type_t picker_cst)
441{
442 const int width = roi->width;
443 const size_t size = _box_size(box);
444 const size_t stride = 4 * (size_t)(box[2] - box[0]);
445 const size_t off_mul = 4 * width;
446 const size_t off_add = 4 * box[0];
447 const float w = 1.0f / (float)size;
448 const size_t numthreads = darktable.num_openmp_threads;
449
450 size_t allocsize;
451 float *const restrict mean = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
452 float *const restrict mmin = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
453 float *const restrict mmax = dt_pixelpipe_cache_alloc_perthread_float(4, &allocsize);
454
455 if(IS_NULL_PTR(mean) || IS_NULL_PTR(mmax) || IS_NULL_PTR(mmin))
456 goto error;
457
458 for(int n = 0; n < allocsize * numthreads; n++)
459 {
460 mean[n] = 0.0f;
461 mmin[n] = INFINITY;
462 mmax[n] = -INFINITY;
463 }
465 {
466 float *const restrict tmean = dt_get_perthread(mean,allocsize);
467 float *const restrict tmmin = dt_get_perthread(mmin,allocsize);
468 float *const restrict tmmax = dt_get_perthread(mmax,allocsize);
470 for(size_t j = box[1]; j < box[3]; j++)
471 {
472 const size_t offset = j * off_mul + off_add;
473 if(picker_cst == IOP_CS_HSL)
474 _color_picker_direct_hsl(tmean, tmmin, tmmax, pixel + offset, w, stride);
475 else if(picker_cst == IOP_CS_LCH || picker_cst == IOP_CS_JZCZHZ)
476 _color_picker_direct_lch_or_jzczhz(tmean, tmmin, tmmax, pixel + offset, w, stride);
477 else
478 _color_picker_rgb_or_lab(tmean, tmmin, tmmax, pixel + offset, w, stride);
479 }
480 }
481
482 for(int n = 0; n < numthreads; n++)
483 {
484 float *const restrict tmean = dt_get_bythread(mean,allocsize,n);
485 float *const restrict tmmin = dt_get_bythread(mmin,allocsize,n);
486 float *const restrict tmmax = dt_get_bythread(mmax,allocsize,n);
487
489 {
490 picked_color[k] += tmean[k];
491 picked_color_min[k] = fminf(picked_color_min[k], tmmin[k]);
492 picked_color_max[k] = fmaxf(picked_color_max[k], tmmax[k]);
493 }
494 }
495
496error:
500}
501
502static void color_picker_helper_4ch_converted(const float *const pixel, const dt_iop_roi_t *const roi,
503 const int *const box, dt_aligned_pixel_t picked_color,
504 dt_aligned_pixel_t picked_color_min,
505 dt_aligned_pixel_t picked_color_max,
506 const dt_iop_colorspace_type_t picker_cst)
507{
508 if(_box_size(box) > 10000)
509 color_picker_helper_4ch_converted_parallel(pixel, roi, box, picked_color, picked_color_min,
510 picked_color_max, picker_cst);
511 else
512 color_picker_helper_4ch_converted_seq(pixel, roi, box, picked_color, picked_color_min,
513 picked_color_max, picker_cst);
514}
515
516static void color_picker_helper_bayer_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
517 const dt_iop_roi_t *const roi, const int *const box,
518 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
519 dt_aligned_pixel_t picked_color_max)
520{
521 const int width = roi->width;
522 const uint32_t filters = dsc->filters;
523
524 uint32_t weights[4] = { 0u, 0u, 0u, 0u };
525
526 // code path for small region, especially for color picker point mode
527 for(size_t j = box[1]; j < box[3]; j++)
528 {
529 for(size_t i = box[0]; i < box[2]; i++)
530 {
531 const int c = FC(j + roi->y, i + roi->x, filters);
532 const size_t k = width * j + i;
533
534 const float v = pixel[k];
535
536 picked_color[c] += v;
537 picked_color_min[c] = fminf(picked_color_min[c], v);
538 picked_color_max[c] = fmaxf(picked_color_max[c], v);
539 weights[c]++;
540 }
541 }
542
543 // and finally normalize data. For bayer, there is twice as much green.
544 for(int c = 0; c < 4; c++)
545 {
546 picked_color[c] = weights[c] ? (picked_color[c] / (float)weights[c]) : 0.0f;
547 }
548}
549
550static void color_picker_helper_bayer_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
551 const dt_iop_roi_t *const roi, const int *const box,
552 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
553 dt_aligned_pixel_t picked_color_max)
554{
555 const int width = roi->width;
556 const uint32_t filters = dsc->filters;
557
558 uint32_t weights[4] = { 0u, 0u, 0u, 0u };
559
560 const size_t numthreads = darktable.num_openmp_threads;
561
562 //TODO: convert to use dt_pixelpipe_cache_alloc_perthread
563 float *const msum = malloc(sizeof(float) * numthreads * 4);
564 float *const mmin = malloc(sizeof(float) * numthreads * 4);
565 float *const mmax = malloc(sizeof(float) * numthreads * 4);
566 uint32_t *const cnt = malloc(sizeof(uint32_t) * numthreads * 4);
567
568 if(IS_NULL_PTR(msum) || IS_NULL_PTR(mmin) || IS_NULL_PTR(mmax) || IS_NULL_PTR(cnt))
569 goto error;
570
571 for(int n = 0; n < 4 * numthreads; n++)
572 {
573 msum[n] = 0.0f;
574 mmin[n] = INFINITY;
575 mmax[n] = -INFINITY;
576 cnt[n] = 0u;
577 }
578 __OMP_PARALLEL__(num_threads(numthreads))
579 {
580 const int tnum = dt_get_thread_num();
581
582 float *const tsum = msum + 4 * tnum;
583 float *const tmmin = mmin + 4 * tnum;
584 float *const tmmax = mmax + 4 * tnum;
585 uint32_t *const tcnt = cnt + 4 * tnum;
586 __OMP_FOR__(collapse(2))
587 for(size_t j = box[1]; j < box[3]; j++)
588 {
589 for(size_t i = box[0]; i < box[2]; i++)
590 {
591 const int c = FC(j + roi->y, i + roi->x, filters);
592 const size_t k = width * j + i;
593
594 const float v = pixel[k];
595
596 tsum[c] += v;
597 tmmin[c] = fminf(tmmin[c], v);
598 tmmax[c] = fmaxf(tmmax[c], v);
599 tcnt[c]++;
600 }
601 }
602 }
603
604 for(int n = 0; n < numthreads; n++)
605 {
606 for(int c = 0; c < 4; c++)
607 {
608 picked_color[c] += msum[4 * n + c];
609 picked_color_min[c] = fminf(picked_color_min[c], mmin[4 * n + c]);
610 picked_color_max[c] = fmaxf(picked_color_max[c], mmax[4 * n + c]);
611 weights[c] += cnt[4 * n + c];
612 }
613 }
614
615 // and finally normalize data. For bayer, there is twice as much green.
616 for(int c = 0; c < 4; c++)
617 {
618 picked_color[c] = weights[c] ? (picked_color[c] / (float)weights[c]) : 0.0f;
619 }
620
621error:;
622 dt_free(cnt);
623 dt_free(mmax);
624 dt_free(mmin);
625 dt_free(msum);
626}
627
628static void color_picker_helper_bayer(const dt_iop_buffer_dsc_t *dsc, const float *const pixel,
629 const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color,
630 dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
631{
632 const size_t size = _box_size(box);
633
634 if(size > 100) // avoid inefficient multi-threading in case of small region size (arbitrary limit)
635 return color_picker_helper_bayer_parallel(dsc, pixel, roi, box, picked_color, picked_color_min,
636 picked_color_max);
637 else
638 return color_picker_helper_bayer_seq(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
639}
640
641static void color_picker_helper_xtrans_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
642 const dt_iop_roi_t *const roi, const int *const box,
643 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
644 dt_aligned_pixel_t picked_color_max)
645{
646 const int width = roi->width;
647 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])dsc->xtrans;
648
649 uint32_t weights[3] = { 0u, 0u, 0u };
650
651 // code path for small region, especially for color picker point mode
652 for(size_t j = box[1]; j < box[3]; j++)
653 {
654 for(size_t i = box[0]; i < box[2]; i++)
655 {
656 const int c = FCxtrans(j, i, roi, xtrans);
657 const size_t k = width * j + i;
658
659 const float v = pixel[k];
660
661 picked_color[c] += v;
662 picked_color_min[c] = fminf(picked_color_min[c], v);
663 picked_color_max[c] = fmaxf(picked_color_max[c], v);
664 weights[c]++;
665 }
666 }
667
668 // and finally normalize data.
669 // X-Trans RGB weighting averages to 2:5:2 for each 3x3 cell
670 for(int c = 0; c < 3; c++)
671 {
672 picked_color[c] /= (float)weights[c];
673 }
674}
675
676static void color_picker_helper_xtrans_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel,
677 const dt_iop_roi_t *const roi, const int *const box,
678 dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
679 dt_aligned_pixel_t picked_color_max)
680{
681 const int width = roi->width;
682 const uint8_t(*const xtrans)[6] = (const uint8_t(*const)[6])dsc->xtrans;
683
684 uint32_t weights[3] = { 0u, 0u, 0u };
685
686 const size_t numthreads = MAX(1, (size_t)omp_get_max_threads());
687
688 //TODO: convert to use dt_pixelpipe_cache_alloc_perthread
689 float *const mmin = malloc(sizeof(float) * numthreads * 3);
690 float *const msum = malloc(sizeof(float) * numthreads * 3);
691 float *const mmax = malloc(sizeof(float) * numthreads * 3);
692 uint32_t *const cnt = malloc(sizeof(uint32_t) * numthreads * 3);
693
694 if(IS_NULL_PTR(mmin) || IS_NULL_PTR(msum) || IS_NULL_PTR(mmax) || IS_NULL_PTR(cnt))
695 goto error;
696
697
698 for(int n = 0; n < 3 * numthreads; n++)
699 {
700 msum[n] = 0.0f;
701 mmin[n] = INFINITY;
702 mmax[n] = -INFINITY;
703 cnt[n] = 0u;
704 }
705 __OMP_PARALLEL__(num_threads(numthreads))
706 {
707 const int tnum = dt_get_thread_num();
708
709 float *const tsum = msum + 3 * tnum;
710 float *const tmmin = mmin + 3 * tnum;
711 float *const tmmax = mmax + 3 * tnum;
712 uint32_t *const tcnt = cnt + 3 * tnum;
713 __OMP_FOR__(collapse(2))
714 for(size_t j = box[1]; j < box[3]; j++)
715 {
716 for(size_t i = box[0]; i < box[2]; i++)
717 {
718 const int c = FCxtrans(j, i, roi, xtrans);
719 const size_t k = width * j + i;
720
721 const float v = pixel[k];
722
723 tsum[c] += v;
724 tmmin[c] = fminf(tmmin[c], v);
725 tmmax[c] = fmaxf(tmmax[c], v);
726 tcnt[c]++;
727 }
728 }
729 }
730
731 for(int n = 0; n < numthreads; n++)
732 {
733 for(int c = 0; c < 3; c++)
734 {
735 picked_color[c] += msum[3 * n + c];
736 picked_color_min[c] = fminf(picked_color_min[c], mmin[3 * n + c]);
737 picked_color_max[c] = fmaxf(picked_color_max[c], mmax[3 * n + c]);
738 weights[c] += cnt[3 * n + c];
739 }
740 }
741
742 // and finally normalize data.
743 // X-Trans RGB weighting averages to 2:5:2 for each 3x3 cell
744 for(int c = 0; c < 3; c++)
745 {
746 picked_color[c] /= (float)weights[c];
747 }
748
749error:;
750 dt_free(cnt);
751 dt_free(mmax);
752 dt_free(mmin);
753 dt_free(msum);
754}
755
756static void color_picker_helper_xtrans(const dt_iop_buffer_dsc_t *dsc, const float *const pixel,
757 const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color,
758 dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
759{
760 const size_t size = _box_size(box);
761
762 if(size > 100) // avoid inefficient multi-threading in case of small region size (arbitrary limit)
763 return color_picker_helper_xtrans_parallel(dsc, pixel, roi, box, picked_color, picked_color_min,
764 picked_color_max);
765 else
766 return color_picker_helper_xtrans_seq(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
767}
768
769// picked_color, picked_color_min and picked_color_max should be aligned
770void dt_color_picker_helper(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi,
771 const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min,
772 dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t image_cst,
773 const dt_iop_colorspace_type_t picker_cst,
774 const dt_iop_order_iccprofile_info_t *const profile)
775{
776 dt_times_t start_time = { 0 }, end_time = { 0 };
777 if(darktable.unmuted & DT_DEBUG_PERF) dt_get_times(&start_time);
778
779 if(dsc->channels == 4u)
780 {
781 // Denoise the image
782 size_t padded_size;
783 float *const restrict denoised = dt_pixelpipe_cache_alloc_align_float_cache(4 * roi->width * roi->height, 0);
784 float *converted = NULL;
785 float *const tempbuf = dt_pixelpipe_cache_alloc_perthread_float(4 * roi->width, &padded_size); // TODO: alloc in caller
786 if(IS_NULL_PTR(tempbuf) || IS_NULL_PTR(denoised))
787 goto error;
788
789 // blur without clipping negatives because Lab a and b channels can be legitimately negative
790 blur_2D_Bspline(pixel, denoised, tempbuf, roi->width, roi->height, 1, FALSE);
791
792 if(((image_cst == picker_cst) || (picker_cst == IOP_CS_NONE)))
793 color_picker_helper_4ch(dsc, denoised, roi, box, picked_color, picked_color_min, picked_color_max, picker_cst, profile);
794 else if((image_cst == IOP_CS_LAB
795 && (picker_cst == IOP_CS_LCH || picker_cst == IOP_CS_RGB))
796 || (dt_iop_colorspace_is_rgb(image_cst)
797 && (picker_cst == IOP_CS_LAB || picker_cst == IOP_CS_HSL || picker_cst == IOP_CS_JZCZHZ)))
798 {
799 /* The picker samples module input/output buffers after the previous piece has written them.
800 When the requested picker colorspace differs from that buffer colorspace, we need a real
801 conversion before averaging. Falling back to raw channel statistics would silently report
802 Lab values as RGB (or the other way around), which makes module-side picker feedback wrong. */
803 converted = dt_pixelpipe_cache_alloc_align_float_cache(4 * roi->width * roi->height, 0);
804 if(IS_NULL_PTR(converted))
805 goto error;
806
807 _color_picker_convert_buffer(denoised, converted, (size_t)roi->width * roi->height, image_cst, picker_cst, profile);
808 color_picker_helper_4ch_converted(converted, roi, box, picked_color, picked_color_min, picked_color_max, picker_cst);
809 }
810 else // This is a fallback, better than crashing as happens with monochromes
811 color_picker_helper_4ch(dsc, denoised, roi, box, picked_color, picked_color_min, picked_color_max, picker_cst, profile);
812
813 error:;
817 }
818 else if(dsc->channels == 1u && dsc->filters != 0u && dsc->filters != 9u)
819 color_picker_helper_bayer(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
820 else if(dsc->channels == 1u && dsc->filters == 9u)
821 color_picker_helper_xtrans(dsc, pixel, roi, box, picked_color, picked_color_min, picked_color_max);
822 else
824
826 {
827 dt_get_times(&end_time);
828 fprintf(stderr, "colorpicker stats reading took %.3f secs (%.3f CPU)\n",
829 end_time.clock - start_time.clock, end_time.user - start_time.user);
830 }
831}
832
833// clang-format off
834// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
835// vim: shiftwidth=2 expandtab tabstop=2 cindent
836// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
837// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:202
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
static void blur_2D_Bspline(const float *const restrict in, float *const restrict out, float *const restrict tempbuf, const size_t width, const size_t height, const int mult, const gboolean clip_negatives)
Definition bspline.h:325
dt_iop_colorspace_type_t
@ IOP_CS_LCH
@ IOP_CS_JZCZHZ
@ IOP_CS_RGB
@ IOP_CS_HSL
@ IOP_CS_LAB
@ IOP_CS_NONE
static void _color_picker_convert_buffer(const float *const restrict input, float *const restrict output, const size_t pixels, const dt_iop_colorspace_type_t image_cst, const dt_iop_colorspace_type_t picker_cst, const dt_iop_order_iccprofile_info_t *const profile)
Convert a 4-channel sampling buffer into the picker colorspace.
static size_t _box_size(const int *const box)
static void color_picker_helper_4ch_converted_seq(const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t picker_cst)
static void color_picker_helper_xtrans_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
static void color_picker_helper_4ch_converted(const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t picker_cst)
static void rgb_to_JzCzhz(const dt_aligned_pixel_t rgb, dt_aligned_pixel_t JzCzhz, const dt_iop_order_iccprofile_info_t *const profile)
void dt_color_picker_helper(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t image_cst, const dt_iop_colorspace_type_t picker_cst, const dt_iop_order_iccprofile_info_t *const profile)
static void _color_picker_hsl(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_4ch_converted_parallel(const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t picker_cst)
static void color_picker_helper_bayer(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
static void color_picker_helper_xtrans(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
static void _color_picker_jzczhz(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width, const dt_iop_order_iccprofile_info_t *const profile)
static void _color_picker_direct_lch_or_jzczhz(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_bayer_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
static void _color_picker_direct_hsl(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void _color_picker_lch(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_4ch(const dt_iop_buffer_dsc_t *dsc, const float *const pixel, const dt_iop_roi_t *roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to, const dt_iop_order_iccprofile_info_t *const profile)
static void color_picker_helper_4ch_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to, const dt_iop_order_iccprofile_info_t *const profile)
static void _color_picker_rgb_or_lab(dt_aligned_pixel_t avg, dt_aligned_pixel_t min, dt_aligned_pixel_t max, const float *const pixels, const float w, const size_t width)
static void color_picker_helper_bayer_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
static void color_picker_helper_4ch_parallel(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max, const dt_iop_colorspace_type_t cst_to, const dt_iop_order_iccprofile_info_t *const profile)
static void color_picker_helper_xtrans_seq(const dt_iop_buffer_dsc_t *const dsc, const float *const pixel, const dt_iop_roi_t *const roi, const int *const box, dt_aligned_pixel_t picked_color, dt_aligned_pixel_t picked_color_min, dt_aligned_pixel_t picked_color_max)
static dt_aligned_pixel_t rgb
static const float const float const float min
const float max
static dt_aligned_pixel_t XYZ_D65
static dt_aligned_pixel_t XYZ_D50
static dt_aligned_pixel_t JzAzBz
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
darktable_t darktable
Definition darktable.c:181
@ DT_DEBUG_PERF
Definition darktable.h:719
#define dt_get_bythread(buf, padsize, tnum)
Definition darktable.h:1038
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
#define __OMP_FOR__(...)
Definition darktable.h:261
#define omp_get_max_threads()
Definition darktable.h:254
static int dt_get_thread_num()
Definition darktable.h:291
#define dt_free(ptr)
Definition darktable.h:456
static void dt_get_times(dt_times_t *t)
Definition darktable.h:921
#define __OMP_DECLARE_SIMD__(...)
Definition darktable.h:263
#define __OMP_PARALLEL__(...)
Definition darktable.h:257
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define dt_get_perthread(buf, padsize)
Definition darktable.h:1035
#define for_four_channels(_var,...)
Definition darktable.h:664
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define dt_pixelpipe_cache_alloc_perthread_float(n, padded_size)
Definition darktable.h:1030
#define dt_unreachable_codepath()
Definition darktable.h:979
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
static int FCxtrans(const int row, const int col, global const unsigned char(*const xtrans)[6])
static int FC(const int row, const int col, const unsigned int filters)
static gboolean dt_iop_colorspace_is_rgb(const dt_iop_colorspace_type_t cst)
Definition imageop.h:213
const float v
float *const restrict const size_t k
size_t size
Definition mipmap_cache.c:3
float dt_aligned_pixel_t[4]
int32_t num_openmp_threads
Definition darktable.h:758
int32_t unmuted
Definition darktable.h:760
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
uint8_t xtrans[6][6]
Definition format.h:70
dt_colormatrix_t matrix_out_transposed
Definition iop_profile.h:66
dt_colormatrix_t matrix_in_transposed
Definition iop_profile.h:65
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double clock
Definition darktable.h:842
double user
Definition darktable.h:843
#define MAX(a, b)
Definition thinplate.c:29