Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
common/histogram.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2014-2016 Roman Lebedev.
4 Copyright (C) 2014, 2016 Tobias Ellinghaus.
5 Copyright (C) 2018-2019 Edgardo Hoszowski.
6 Copyright (C) 2019 Andreas Schneider.
7 Copyright (C) 2020 Pascal Obry.
8 Copyright (C) 2021 Ralf Brown.
9 Copyright (C) 2022 Martin Baƙinka.
10 Copyright (C) 2022 Philipp Lutz.
11
12 darktable is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 darktable is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with darktable. If not, see <http://www.gnu.org/licenses/>.
24*/
25#include <math.h>
26#include <stddef.h>
27#include <stdint.h>
28#include <assert.h>
29#include <stdlib.h>
30
32#include "system/macros.h"
33#include "system/mem_alloc.h"
34#include "system/openmp.h"
35#include "system/simd.h"
37#include "common/histogram.h"
38#include "develop/imageop.h"
39
40#define S(V, params) ((params->mul) * ((float)V))
41#define P(V, params) (CLAMP((V), 0, (params->bins_count - 1)))
42#define PU(V, params) (MIN((V), (params->bins_count - 1)))
43#define PS(V, params) (P(S(V, params), params))
44
45//------------------------------------------------------------------------------
46
48 const dt_dev_histogram_collection_params_t *const histogram_params, const float *pixel, uint32_t *histogram)
49{
50 const uint32_t i = PS(*pixel, histogram_params);
51 histogram[4 * i]++;
52}
53
54inline static void histogram_helper_cs_RAW(const dt_dev_histogram_collection_params_t *const histogram_params,
55 const void *pixel, uint32_t *histogram, int j,
56 const dt_iop_order_iccprofile_info_t *const profile_info)
57{
58 const dt_histogram_roi_t *roi = histogram_params->roi;
59 const float *input = (float *)pixel + roi->width * j + roi->crop_x;
60 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, input++)
61 {
62 histogram_helper_cs_RAW_helper_process_pixel_float(histogram_params, input, histogram);
63 }
64}
65
66//------------------------------------------------------------------------------
67
68// WARNING: you must ensure that bins_count is big enough
70 const dt_dev_histogram_collection_params_t *const histogram_params, const uint16_t *pixel, uint32_t *histogram)
71{
72 const uint16_t i = PU(*pixel, histogram_params);
73 histogram[4 * i]++;
74}
75
77 const void *pixel, uint32_t *histogram, int j,
78 const dt_iop_order_iccprofile_info_t *const profile_info)
79{
80 const dt_histogram_roi_t *roi = histogram_params->roi;
81 uint16_t *in = (uint16_t *)pixel + roi->width * j + roi->crop_x;
82
83 // process pixels
84 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, in++)
85 histogram_helper_cs_RAW_helper_process_pixel_uint16(histogram_params, in, histogram);
86}
87
88//------------------------------------------------------------------------------
89
90inline static void __attribute__((__unused__)) histogram_helper_cs_rgb_helper_process_pixel_float(
91 const dt_dev_histogram_collection_params_t *const histogram_params, const float *pixel, uint32_t *histogram)
92{
93 const uint32_t R = PS(pixel[0], histogram_params);
94 const uint32_t G = PS(pixel[1], histogram_params);
95 const uint32_t B = PS(pixel[2], histogram_params);
96 histogram[4 * R]++;
97 histogram[4 * G + 1]++;
98 histogram[4 * B + 2]++;
99}
100
101inline static void __attribute__((__unused__)) histogram_helper_cs_rgb_helper_process_pixel_float_compensated(
102 const dt_dev_histogram_collection_params_t *const histogram_params, const float *pixel, uint32_t *histogram,
103 const dt_iop_order_iccprofile_info_t *const profile_info)
104{
105 const dt_aligned_pixel_t rgb = { dt_ioppr_compensate_middle_grey(pixel[0], profile_info),
106 dt_ioppr_compensate_middle_grey(pixel[1], profile_info),
107 dt_ioppr_compensate_middle_grey(pixel[2], profile_info) };
108 const uint32_t R = PS(rgb[0], histogram_params);
109 const uint32_t G = PS(rgb[1], histogram_params);
110 const uint32_t B = PS(rgb[2], histogram_params);
111 histogram[4 * R]++;
112 histogram[4 * G + 1]++;
113 histogram[4 * B + 2]++;
114}
115
116inline static void histogram_helper_cs_rgb(const dt_dev_histogram_collection_params_t *const histogram_params,
117 const void *pixel, uint32_t *histogram, int j,
118 const dt_iop_order_iccprofile_info_t *const profile_info)
119{
120 const dt_histogram_roi_t *roi = histogram_params->roi;
121 float *in = (float *)pixel + 4 * (roi->width * j + roi->crop_x);
122
123 // process aligned pixels with SSE
124 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, in += 4)
125 {
126 histogram_helper_cs_rgb_helper_process_pixel_float(histogram_params, in, histogram);
127 }
128}
129
130inline static void histogram_helper_cs_rgb_compensated(const dt_dev_histogram_collection_params_t *const histogram_params,
131 const void *pixel, uint32_t *histogram, int j,
132 const dt_iop_order_iccprofile_info_t *const profile_info)
133{
134 const dt_histogram_roi_t *roi = histogram_params->roi;
135 float *in = (float *)pixel + 4 * (roi->width * j + roi->crop_x);
136
137 // process aligned pixels with SSE
138 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, in += 4)
139 {
140 histogram_helper_cs_rgb_helper_process_pixel_float_compensated(histogram_params, in, histogram, profile_info);
141 }
142}
143
144//------------------------------------------------------------------------------
145
146inline static void __attribute__((__unused__)) histogram_helper_cs_Lab_helper_process_pixel_float(
147 const dt_dev_histogram_collection_params_t *const histogram_params, const float *pixel, uint32_t *histogram)
148{
149 const float Lv = pixel[0];
150 const float av = pixel[1];
151 const float bv = pixel[2];
152 const float max = histogram_params->bins_count - 1;
153 const uint32_t L = CLAMP(histogram_params->mul / 100.0f * (Lv), 0, max);
154 const uint32_t a = CLAMP(histogram_params->mul / 256.0f * (av + 128.0f), 0, max);
155 const uint32_t b = CLAMP(histogram_params->mul / 256.0f * (bv + 128.0f), 0, max);
156 histogram[4 * L]++;
157 histogram[4 * a + 1]++;
158 histogram[4 * b + 2]++;
159}
160
161inline static void histogram_helper_cs_Lab(const dt_dev_histogram_collection_params_t *const histogram_params,
162 const void *pixel, uint32_t *histogram, int j,
163 const dt_iop_order_iccprofile_info_t *const profile_info)
164{
165 const dt_histogram_roi_t *roi = histogram_params->roi;
166 float *in = (float *)pixel + 4 * (roi->width * j + roi->crop_x);
167
168 // process aligned pixels with SSE
169 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, in += 4)
170 {
171 histogram_helper_cs_Lab_helper_process_pixel_float(histogram_params, in, histogram);
172 }
173}
174
175inline static void __attribute__((__unused__)) histogram_helper_cs_Lab_LCh_helper_process_pixel_float(
176 const dt_dev_histogram_collection_params_t *const histogram_params, const float *pixel, uint32_t *histogram)
177{
179 dt_Lab_2_LCH(pixel, LCh);
180 const uint32_t L = PS((LCh[0] / 100.f), histogram_params);
181 const uint32_t C = PS((LCh[1] / (128.0f * sqrtf(2.0f))), histogram_params);
182 const uint32_t h = PS(LCh[2], histogram_params);
183 histogram[4 * L]++;
184 histogram[4 * C + 1]++;
185 histogram[4 * h + 2]++;
186}
187
188inline static void histogram_helper_cs_Lab_LCh(const dt_dev_histogram_collection_params_t *const histogram_params,
189 const void *pixel, uint32_t *histogram, int j,
190 const dt_iop_order_iccprofile_info_t *const profile_info)
191{
192 const dt_histogram_roi_t *roi = histogram_params->roi;
193 float *in = (float *)pixel + 4 * (roi->width * j + roi->crop_x);
194
195 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, in += 4)
196 {
197 histogram_helper_cs_Lab_LCh_helper_process_pixel_float(histogram_params, in, histogram);
198 }
199}
200
201inline static void histogram_helper_cs_LCh(const dt_dev_histogram_collection_params_t *const histogram_params,
202 const void *pixel, uint32_t *histogram, int j,
203 const dt_iop_order_iccprofile_info_t *const profile_info)
204{
205 const dt_histogram_roi_t *roi = histogram_params->roi;
206 float *in = (float *)pixel + 4 * (roi->width * j + roi->crop_x);
207
208 for(int i = 0; i < roi->width - roi->crop_width - roi->crop_x; i++, in += 4)
209 {
210 const uint32_t L = PS((in[0] / 100.f), histogram_params);
211 const uint32_t C = PS((in[1] / (128.0f * sqrtf(2.0f))), histogram_params);
212 const uint32_t h = PS(in[2], histogram_params);
213 histogram[4 * L]++;
214 histogram[4 * C + 1]++;
215 histogram[4 * h + 2]++;
216 }
217}
218
219//==============================================================================
220
222 dt_dev_histogram_stats_t *histogram_stats, const void *const pixel,
223 uint32_t **histogram, const dt_worker Worker,
224 const dt_iop_order_iccprofile_info_t *const profile_info)
225{
226 const int nthreads = omp_get_max_threads();
227
228 const size_t bins_total = (size_t)4 * histogram_params->bins_count;
229 const size_t buf_size = bins_total * sizeof(uint32_t);
230 void *partial_hists = calloc(nthreads, buf_size);
231
232 if(histogram_params->mul == 0) histogram_params->mul = (double)(histogram_params->bins_count - 1);
233
234 const dt_histogram_roi_t *const roi = histogram_params->roi;
235 __OMP_PARALLEL_FOR__(shared(partial_hists) )
236 for(int j = roi->crop_y; j < roi->height - roi->crop_height; j++)
237 {
238 uint32_t *thread_hist = (uint32_t *)partial_hists + bins_total * omp_get_thread_num();
239 Worker(histogram_params, pixel, thread_hist, j, profile_info);
240 }
241
242#ifdef _OPENMP
243 *histogram = realloc(*histogram, buf_size);
244 memset(*histogram, 0, buf_size);
245 uint32_t *hist = *histogram;
246
247#pragma omp parallel for default(firstprivate) \
248 shared(hist, partial_hists)
249 for(size_t k = 0; k < bins_total; k++)
250 {
251 for(size_t n = 0; n < nthreads; n++)
252 {
253 const uint32_t *thread_hist = (uint32_t *)partial_hists + bins_total * n;
254 hist[k] += thread_hist[k];
255 }
256 }
257#else
258 *histogram = realloc(*histogram, buf_size);
259 memmove(*histogram, partial_hists, buf_size);
260#endif
261 dt_free(partial_hists);
262
263 histogram_stats->bins_count = histogram_params->bins_count;
264 histogram_stats->pixels = (roi->width - roi->crop_width - roi->crop_x)
265 * (roi->height - roi->crop_height - roi->crop_y);
266}
267
268//------------------------------------------------------------------------------
269
271 dt_dev_histogram_stats_t *histogram_stats, const dt_iop_colorspace_type_t cst,
272 const dt_iop_colorspace_type_t cst_to, const void *pixel, uint32_t **histogram,
273 const int compensate_middle_grey, const dt_iop_order_iccprofile_info_t *const profile_info)
274{
275 float *converted = NULL;
276 if(cst == IOP_CS_LAB && cst_to == IOP_CS_LCH)
277 {
278 const dt_histogram_roi_t *roi = histogram_params->roi;
279 const size_t pixels = (size_t)roi->width * roi->height;
280 converted = dt_pixelpipe_cache_alloc_align_float_cache(4 * pixels, 0);
281
282 if(!IS_NULL_PTR(converted))
283 {
285 for(size_t k = 0; k < pixels; k++)
286 {
287 const size_t offset = 4 * k;
288 dt_Lab_2_LCH((const float *)pixel + offset, converted + offset);
289 converted[offset + 3] = ((const float *)pixel)[offset + 3];
290 }
291 }
292 }
293
294 switch(cst)
295 {
296 case IOP_CS_RAW:
297 dt_histogram_worker(histogram_params, histogram_stats, pixel, histogram, histogram_helper_cs_RAW, profile_info);
298 histogram_stats->ch = 1u;
299 break;
300
301 case IOP_CS_RGB:
303 if(compensate_middle_grey && profile_info)
304 dt_histogram_worker(histogram_params, histogram_stats, pixel, histogram, histogram_helper_cs_rgb_compensated, profile_info);
305 else
306 dt_histogram_worker(histogram_params, histogram_stats, pixel, histogram, histogram_helper_cs_rgb, profile_info);
307 histogram_stats->ch = 3u;
308 break;
309
310 case IOP_CS_LAB:
311 default:
312 if(cst_to != IOP_CS_LCH)
313 dt_histogram_worker(histogram_params, histogram_stats, pixel, histogram, histogram_helper_cs_Lab, profile_info);
314 else if(!IS_NULL_PTR(converted))
315 dt_histogram_worker(histogram_params, histogram_stats, converted, histogram, histogram_helper_cs_LCh, profile_info);
316 else
317 dt_histogram_worker(histogram_params, histogram_stats, pixel, histogram, histogram_helper_cs_Lab_LCh, profile_info);
318 histogram_stats->ch = 3u;
319 break;
320 }
321
323}
324
325void dt_histogram_max_helper(const dt_dev_histogram_stats_t *const histogram_stats,
327 uint32_t **histogram, uint32_t *histogram_max)
328{
329 if(IS_NULL_PTR(*histogram)) return;
330 histogram_max[0] = histogram_max[1] = histogram_max[2] = histogram_max[3] = 0;
331 uint32_t *hist = *histogram;
332 switch(cst)
333 {
334 case IOP_CS_RAW:
335 for(int k = 0; k < 4 * histogram_stats->bins_count; k += 4)
336 histogram_max[0] = histogram_max[0] > hist[k] ? histogram_max[0] : hist[k];
337 break;
338
339 case IOP_CS_RGB:
341 // don't count <= 0 pixels
342 for(int k = 4; k < 4 * histogram_stats->bins_count; k += 4)
343 histogram_max[0] = histogram_max[0] > hist[k] ? histogram_max[0] : hist[k];
344 for(int k = 5; k < 4 * histogram_stats->bins_count; k += 4)
345 histogram_max[1] = histogram_max[1] > hist[k] ? histogram_max[1] : hist[k];
346 for(int k = 6; k < 4 * histogram_stats->bins_count; k += 4)
347 histogram_max[2] = histogram_max[2] > hist[k] ? histogram_max[2] : hist[k];
348 for(int k = 7; k < 4 * histogram_stats->bins_count; k += 4)
349 histogram_max[3] = histogram_max[3] > hist[k] ? histogram_max[3] : hist[k];
350 break;
351
352 case IOP_CS_LAB:
353 default:
354 if(cst_to == IOP_CS_LCH)
355 {
356 // don't count <= 0 pixels
357 for(int k = 4; k < 4 * histogram_stats->bins_count; k += 4)
358 histogram_max[0] = histogram_max[0] > hist[k] ? histogram_max[0] : hist[k];
359 for(int k = 5; k < 4 * histogram_stats->bins_count; k += 4)
360 histogram_max[1] = histogram_max[1] > hist[k] ? histogram_max[1] : hist[k];
361 for(int k = 6; k < 4 * histogram_stats->bins_count; k += 4)
362 histogram_max[2] = histogram_max[2] > hist[k] ? histogram_max[2] : hist[k];
363 for(int k = 7; k < 4 * histogram_stats->bins_count; k += 4)
364 histogram_max[3] = histogram_max[3] > hist[k] ? histogram_max[3] : hist[k];
365 }
366 else
367 {
368 // don't count <= 0 pixels in L
369 for(int k = 4; k < 4 * histogram_stats->bins_count; k += 4)
370 histogram_max[0] = histogram_max[0] > hist[k] ? histogram_max[0] : hist[k];
371
372 // don't count <= -128 and >= +128 pixels in a and b
373 for(int k = 5; k < 4 * (histogram_stats->bins_count - 1); k += 4)
374 histogram_max[1] = histogram_max[1] > hist[k] ? histogram_max[1] : hist[k];
375 for(int k = 6; k < 4 * (histogram_stats->bins_count - 1); k += 4)
376 histogram_max[2] = histogram_max[2] > hist[k] ? histogram_max[2] : hist[k];
377 }
378 break;
379 }
380}
381
382// clang-format off
383// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
384// vim: shiftwidth=2 expandtab tabstop=2 cindent
385// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
386// clang-format on
dt_iop_colorspace_type_t
@ IOP_CS_RAW
@ IOP_CS_LCH
@ IOP_CS_RGB
@ IOP_CS_LAB
#define B(y, x)
static dt_aligned_pixel_t rgb
const float max
static const float const float C
static void histogram_helper_cs_Lab_LCh(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
static void histogram_helper_cs_RAW_helper_process_pixel_float(const dt_dev_histogram_collection_params_t *const histogram_params, const float *pixel, uint32_t *histogram)
static void histogram_helper_cs_rgb(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
static void histogram_helper_cs_RAW_helper_process_pixel_uint16(const dt_dev_histogram_collection_params_t *const histogram_params, const uint16_t *pixel, uint32_t *histogram)
static void histogram_helper_cs_RAW(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
#define PS(V, params)
void dt_histogram_max_helper(const dt_dev_histogram_stats_t *const histogram_stats, const dt_iop_colorspace_type_t cst, const dt_iop_colorspace_type_t cst_to, uint32_t **histogram, uint32_t *histogram_max)
void dt_histogram_worker(dt_dev_histogram_collection_params_t *const histogram_params, dt_dev_histogram_stats_t *histogram_stats, const void *const pixel, uint32_t **histogram, const dt_worker Worker, const dt_iop_order_iccprofile_info_t *const profile_info)
void dt_histogram_helper(dt_dev_histogram_collection_params_t *histogram_params, dt_dev_histogram_stats_t *histogram_stats, const dt_iop_colorspace_type_t cst, const dt_iop_colorspace_type_t cst_to, const void *pixel, uint32_t **histogram, const int compensate_middle_grey, const dt_iop_order_iccprofile_info_t *const profile_info)
static void histogram_helper_cs_LCh(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
static void histogram_helper_cs_rgb_compensated(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
#define PU(V, params)
void dt_histogram_helper_cs_RAW_uint16(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
static void histogram_helper_cs_Lab(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
@ IOP_CS_RGB_DISPLAY
Definition format.h:71
float *const restrict const size_t k
#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 macros.h:96
#define R
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
#define omp_get_max_threads()
Definition openmp.h:90
#define omp_get_thread_num()
Definition openmp.h:91
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
const struct dt_histogram_roi_t * roi
Definition pixelpipe.h:60
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
typedef double((*spd)(unsigned long int wavelength, double TempK))