Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
illuminants.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#pragma once
20
22#include "common/image.h"
23
24
25/* Standard CIE illuminants */
26typedef enum dt_illuminant_t
27{
28 DT_ILLUMINANT_PIPE = 0, // $DESCRIPTION: "same as pipeline (D50)"
29 DT_ILLUMINANT_A = 1, // $DESCRIPTION: "A (incandescent)"
30 DT_ILLUMINANT_D = 2, // $DESCRIPTION: "D (daylight)"
31 DT_ILLUMINANT_E = 3, // $DESCRIPTION: "E (equi-energy)" (x = y)
32 DT_ILLUMINANT_F = 4, // $DESCRIPTION: "F (fluorescent)"
33 DT_ILLUMINANT_LED = 5, // $DESCRIPTION: "LED (LED light)"
34 DT_ILLUMINANT_BB = 6, // $DESCRIPTION: "Planckian (black body)" general black body radiator - not CIE standard
35 DT_ILLUMINANT_CUSTOM = 7, // $DESCRIPTION: "custom" input x and y directly - bypass search
36 DT_ILLUMINANT_DETECT_SURFACES = 8, // $DESCRIPTION: "(AI) detect from image surfaces..." auto-detection in image from grey world model
37 DT_ILLUMINANT_DETECT_EDGES = 9, // $DESCRIPTION: "(AI) detect from image edges..."auto-detection in image from grey edges model
38 DT_ILLUMINANT_CAMERA = 10,// $DESCRIPTION: "as shot in camera" read RAW EXIF for WB
41
42// CIE fluorescent standards : https://en.wikipedia.org/wiki/Standard_illuminant
44{
45 DT_ILLUMINANT_FLUO_F1 = 0, // $DESCRIPTION: "F1 (Daylight 6430 K) - medium CRI"
46 DT_ILLUMINANT_FLUO_F2 = 1, // $DESCRIPTION: "F2 (Cool White 4230 K) - medium CRI"
47 DT_ILLUMINANT_FLUO_F3 = 2, // $DESCRIPTION: "F3 (White 3450 K) - medium CRI"
48 DT_ILLUMINANT_FLUO_F4 = 3, // $DESCRIPTION: "F4 (Warm White 2940 K) - medium CRI"
49 DT_ILLUMINANT_FLUO_F5 = 4, // $DESCRIPTION: "F5 (Daylight 6350 K) - medium CRI"
50 DT_ILLUMINANT_FLUO_F6 = 5, // $DESCRIPTION: "F6 (Lite White 4150 K) - medium CRI"
51 DT_ILLUMINANT_FLUO_F7 = 6, // $DESCRIPTION: "F7 (D65 simulator 6500 K) - high CRI"
52 DT_ILLUMINANT_FLUO_F8 = 7, // $DESCRIPTION: "F8 (D50 simulator 5000 K) - high CRI"
53 DT_ILLUMINANT_FLUO_F9 = 8, // $DESCRIPTION: "F9 (Cool White Deluxe 4150 K) - high CRI"
54 DT_ILLUMINANT_FLUO_F10 = 9, // $DESCRIPTION: "F10 (Tuned RGB 5000 K) - low CRI" Philips TL85, Ultralume 50
55 DT_ILLUMINANT_FLUO_F11 = 10, // $DESCRIPTION: "F11 (Tuned RGB 4000 K) - low CRI" Philips TL84, Ultralume 40
56 DT_ILLUMINANT_FLUO_F12 = 11, // $DESCRIPTION: "F12 (Tuned RGB 3000 K) - low CRI" Philips TL83, Ultralume 30
59
60// CIE LED standards : https://en.wikipedia.org/wiki/Standard_illuminant
62{
63 DT_ILLUMINANT_LED_B1 = 0, // $DESCRIPTION: "B1 (Blue 2733 K)" phosphor-converted blue
64 DT_ILLUMINANT_LED_B2 = 1, // $DESCRIPTION: "B2 (Blue 2998 K)" phosphor-converted blue
65 DT_ILLUMINANT_LED_B3 = 2, // $DESCRIPTION: "B3 (Blue 4103 K)" phosphor-converted blue
66 DT_ILLUMINANT_LED_B4 = 3, // $DESCRIPTION: "B4 (Blue 5109 K)" phosphor-converted blue
67 DT_ILLUMINANT_LED_B5 = 4, // $DESCRIPTION: "B5 (Blue 6598 K)" phosphor-converted blue
68 DT_ILLUMINANT_LED_BH1 = 5, // $DESCRIPTION: "BH1 (Blue-Red hybrid 2851 K)" mix of phosphor-converted blue red
69 DT_ILLUMINANT_LED_RGB1= 6, // $DESCRIPTION: "RGB1 (RGB 2840 K)" mixing of red, green, and blue LEDs
70 DT_ILLUMINANT_LED_V1 = 7, // $DESCRIPTION: "V1 (Violet 2724 K)" phosphor-converted violet
71 DT_ILLUMINANT_LED_V2 = 8, // $DESCRIPTION: "V2 (Violet 4070 K)" phosphor-converted violet
74
75
85 // x_2 // y_2
86 static float fluorescent[DT_ILLUMINANT_FLUO_LAST][2] = { { 0.31310f, 0.33727f }, // DT_ILLUMINANT_FLUO_F1
87 { 0.37208f, 0.37529f }, // DT_ILLUMINANT_FLUO_F2
88 { 0.40910f, 0.39430f }, // DT_ILLUMINANT_FLUO_F3
89 { 0.44018f, 0.40329f }, // DT_ILLUMINANT_FLUO_F4
90 { 0.31379f, 0.34531f }, // DT_ILLUMINANT_FLUO_F5
91 { 0.37790f, 0.38835f }, // DT_ILLUMINANT_FLUO_F6
92 { 0.31292f, 0.32933f }, // DT_ILLUMINANT_FLUO_F7
93 { 0.34588f, 0.35875f }, // DT_ILLUMINANT_FLUO_F8
94 { 0.37417f, 0.37281f }, // DT_ILLUMINANT_FLUO_F9
95 { 0.34609f, 0.35986f }, // DT_ILLUMINANT_FLUO_F10
96 { 0.38052f, 0.37713f }, // DT_ILLUMINANT_FLUO_F11
97 { 0.43695f, 0.40441f } }; // DT_ILLUMINANT_FLUO_F12
107 // x_2 // y_2
108static float led[DT_ILLUMINANT_LED_LAST][2] = { { 0.4560f, 0.4078f }, // DT_ILLUMINANT_LED_B1
109 { 0.4357f, 0.4012f }, // DT_ILLUMINANT_LED_B2
110 { 0.3756f, 0.3723f }, // DT_ILLUMINANT_LED_B3
111 { 0.3422f, 0.3502f }, // DT_ILLUMINANT_LED_B4
112 { 0.3118f, 0.3236f }, // DT_ILLUMINANT_LED_B5
113 { 0.4474f, 0.4066f }, // DT_ILLUMINANT_LED_BH1
114 { 0.4557f, 0.4211f }, // DT_ILLUMINANT_LED_RGB1
115 { 0.4560f, 0.4548f }, // DT_ILLUMINANT_LED_V1
116 { 0.3781f, 0.3775f }}; // DT_ILLUMINANT_LED_V2
117
118#ifdef _OPENMP
119#pragma omp declare simd
120#endif
121static inline float xy_to_CCT(const float x, const float y)
122{
123 // Try to find correlated color temperature from chromaticity
124 // Valid for 3000 K to 50000 K
125 // Reference : https://www.usna.edu/Users/oceano/raylee/papers/RLee_AO_CCTpaper.pdf
126 // Warning : we throw a number ever if it's grossly off. You need to check the error later.
127 const float n = (x - 0.3366f)/(y - 0.1735f);
128 return -949.86315f + 6253.80338f * expf(-n / 0.92159f) + 28.70599f * expf(-n / 0.20039f) + 0.00004f * expf(-n / 0.07125f);
129}
130
131
132#ifdef _OPENMP
133#pragma omp declare simd
134#endif
135static inline void CCT_to_xy_daylight(const float t, float *x, float *y)
136{
137 // Take correlated color temperature in K and find the closest daylight illuminant in 4000 K - 250000 K
138 float x_temp = 0.f;
139 float y_temp = 0.f;
140
141 if(t >= 4000.f && t <= 7000.0f)
142 x_temp = ((-4.6070e9f / t + 2.9678e6f) / t + 0.09911e3f) / t + 0.244063f;
143 else if(t > 7000.f && t <= 25000.f)
144 x_temp = ((-2.0064e9f / t + 1.9018e6f) / t + 0.24748e3f) / t + 0.237040f;
145
146 y_temp = (-3.f * x_temp + 2.87f) * x_temp - 0.275f;
147
148 *x = x_temp;
149 *y = y_temp;
150}
151
152
153#ifdef _OPENMP
154#pragma omp declare simd
155#endif
156static inline void CCT_to_xy_blackbody(const float t, float *x, float *y)
157{
158 // Take correlated color temperature in K and find the closest blackbody illuminant in 1667 K - 250000 K
159 float x_temp = 0.f;
160 float y_temp = 0.f;
161
162 if(t >= 1667.f && t <= 4000.f)
163 x_temp = ((-0.2661239e9f / t - 0.2343589e6f) / t + 0.8776956e3f) / t + 0.179910f;
164 else if(t > 4000.f && t <= 25000.f)
165 x_temp = ((-3.0258469e9f / t + 2.1070379e6f) / t + 0.2226347e3f) / t + 0.240390f;
166
167 if(t >= 1667.f && t <= 2222.f)
168 y_temp = ((-1.1063814f * x_temp - 1.34811020f) * x_temp + 2.18555832f) * x_temp - 0.20219683f;
169 else if(t > 2222.f && t <= 4000.f)
170 y_temp = ((-0.9549476f * x_temp - 1.37418593f) * x_temp + 2.09137015f) * x_temp - 0.16748867f;
171 else if(t > 4000.f && t <= 25000.f)
172 y_temp = (( 3.0817580f * x_temp - 5.87338670f) * x_temp + 3.75112997f) * x_temp - 0.37001483f;
173
174 *x = x_temp;
175 *y = y_temp;
176}
177
178
179#ifdef _OPENMP
180#pragma omp declare simd
181#endif
182static inline void illuminant_xy_to_XYZ(const float x, const float y, dt_aligned_pixel_t XYZ)
183{
184 XYZ[0] = x / y; // X
185 XYZ[1] = 1.f; // Y is always 1 by definition, for an illuminant
186 XYZ[2] = (1.f - x - y) / y; // Z
187}
188
189
190#ifdef _OPENMP
191#pragma omp declare simd
192#endif
193static inline void illuminant_xy_to_RGB(const float x, const float y, dt_aligned_pixel_t RGB)
194{
195 // Get an sRGB preview of current illuminant
196 dt_aligned_pixel_t XYZ;
197 illuminant_xy_to_XYZ(x, y, XYZ);
198
199 // Fixme : convert to RGB display space instead of sRGB but first the display profile should be global in dt,
200 // not confined to colorout where it gets created/destroyed all the time.
201 dt_XYZ_to_Rec709_D50(XYZ, RGB);
202
203 // Handle gamut clipping
204 const float max_RGB = fmaxf(fmaxf(RGB[0], RGB[1]), RGB[2]);
205 for(int c = 0; c < 3; c++) RGB[c] = fmaxf(RGB[c] / max_RGB, 0.f);
206}
207
208
209#ifdef _OPENMP
210#pragma omp declare simd
211#endif
212static inline void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
213{
214 float x, y;
215 if(t > 4000.f)
216 CCT_to_xy_daylight(t, &x, &y);
217 else
218 CCT_to_xy_blackbody(t, &x, &y);
219
220 illuminant_xy_to_RGB(x, y, RGB);
221}
222
223
224// Fetch image from pipeline and read EXIF for camera RAW WB coeffs
225static inline int find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb,
226 float *chroma_x, float *chroma_y);
227
228
229static inline int illuminant_to_xy(const dt_illuminant_t illuminant, // primary type of illuminant
230 const dt_image_t *img, // image container
231 const dt_aligned_pixel_t custom_wb, // optional user-set WB coeffs
232 float *x_out, float *y_out, // chromaticity output
233 const float t, // temperature in K, if needed
234 const dt_illuminant_fluo_t fluo, // sub-type of fluorescent illuminant, if needed
235 const dt_illuminant_led_t iled) // sub-type of led illuminant, if needed
236{
245 float x = 0.f;
246 float y = 0.f;
247
248 switch(illuminant)
249 {
251 {
252 // darktable default pipeline D50
253 x = 0.34567f;
254 y = 0.35850f;
255 break;
256 }
257 case DT_ILLUMINANT_E:
258 {
259 // Equi-energy - easy-peasy
260 x = y = 1.f / 3.f;
261 break;
262 }
263 case DT_ILLUMINANT_A:
264 {
265 // Special case of Planckian locus for incandescent tungsten bulbs
266 x = 0.44757f;
267 y = 0.40745f;
268 break;
269 }
270 case DT_ILLUMINANT_F:
271 {
272 // Fluorescent lighting - look up
273 if(fluo >= DT_ILLUMINANT_FLUO_LAST) break;
274
275 x = fluorescent[fluo][0];
276 y = fluorescent[fluo][1];
277 break;
278 }
280 {
281 // LED lighting - look up
282 if(iled >= DT_ILLUMINANT_LED_LAST) break;
283
284 x = led[iled][0];
285 y = led[iled][1];
286 break;
287 }
288 case DT_ILLUMINANT_D:
289 {
290 // Adjusted Planckian locus for daylight interpolated by cubic splines
291 // Model valid for T in [4000 ; 25000] K
292 CCT_to_xy_daylight(t, &x, &y);
293 if(y != 0.f && x != 0.f) break;
294 // else t is out of bounds -> use black body model (next case)
295 }
296 case DT_ILLUMINANT_BB:
297 {
298 // General Planckian locus for black body radiator interpolated by cubic splines
299 // Model valid for T in [1667 ; 25000] K
300 CCT_to_xy_blackbody(t, &x, &y);
301 if(y != 0.f && x != 0.f) break;
302 // else t is out of bounds -> use custom/original values (next case)
303 }
305 {
306 // Detect WB from RAW EXIF
307 if(img)
308 if(find_temperature_from_raw_coeffs(img, custom_wb, &x, &y)) break;
309 }
310 case DT_ILLUMINANT_CUSTOM: // leave x and y as-is
314 {
315 return FALSE;
316 }
317 }
318
319 if(x != 0.f && y != 0.f)
320 {
321 *x_out = x;
322 *y_out = y;
323 return TRUE;
324 }
325 else
326 return FALSE;
327}
328
329
330static inline void WB_coeffs_to_illuminant_xy(const float CAM_to_XYZ[4][3], const dt_aligned_pixel_t WB,
331 float *x, float *y)
332{
333 // Find the illuminant chromaticity x y from RAW WB coeffs and camera input matrice
334 dt_aligned_pixel_t XYZ, LMS;
335 // Simulate white point, aka convert (1, 1, 1) in camera space to XYZ
336 // warning : we multiply the transpose of CAM_to_XYZ since the pseudoinverse transposes it
337 XYZ[0] = CAM_to_XYZ[0][0] / WB[0] + CAM_to_XYZ[1][0] / WB[1] + CAM_to_XYZ[2][0] / WB[2];
338 XYZ[1] = CAM_to_XYZ[0][1] / WB[0] + CAM_to_XYZ[1][1] / WB[1] + CAM_to_XYZ[2][1] / WB[2];
339 XYZ[2] = CAM_to_XYZ[0][2] / WB[0] + CAM_to_XYZ[1][2] / WB[1] + CAM_to_XYZ[2][2] / WB[2];
340
341 // Matrices white point is D65. We need to convert it for darktable's pipe (D50)
342 static const dt_aligned_pixel_t D65 = { 0.941238f, 1.040633f, 1.088932f, 0.f };
343 const float p = powf(1.088932f / 0.818155f, 0.0834f);
344
346 bradford_adapt_D50(LMS, D65, p, FALSE, LMS);
348
349 // Get white point chromaticity
350 XYZ[0] /= XYZ[1];
351 XYZ[2] /= XYZ[1];
352 XYZ[1] /= XYZ[1];
353
354 *x = XYZ[0] / (XYZ[0] + XYZ[1] + XYZ[2]);
355 *y = XYZ[1] / (XYZ[0] + XYZ[1] + XYZ[2]);
356}
357
358
359static inline void matrice_pseudoinverse(float (*in)[3], float (*out)[3], int size)
360{
361 float DT_ALIGNED_ARRAY work[3][6];
362
363 for(int i = 0; i < 3; i++)
364 {
365 for(int j = 0; j < 6; j++)
366 work[i][j] = j == i+3;
367 for(int j = 0; j < 3; j++)
368 for(int k = 0; k < size; k++)
369 work[i][j] += in[k][i] * in[k][j];
370 }
371 for(int i = 0; i < 3; i++)
372 {
373 float num = work[i][i];
374 for(int j = 0; j < 6; j++)
375 work[i][j] /= num;
376 for(int k = 0; k < 3; k++)
377 {
378 if(k==i) continue;
379 num = work[k][i];
380 for(int j = 0; j < 6; j++)
381 work[k][j] -= work[i][j] * num;
382 }
383 }
384 for(int i = 0; i < size; i++)
385 for(int j = 0; j < 3; j++)
386 {
387 out[i][j] = 0.0f;
388 for(int k = 0; k < 3; k++)
389 out[i][j] += work[j][k+3] * in[i][k];
390 }
391}
392
393
394static int find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb,
395 float *chroma_x, float *chroma_y)
396{
397 if(img == NULL) return FALSE;
399
400 int has_valid_coeffs = TRUE;
401 const int num_coeffs = (img->flags & DT_IMAGE_4BAYER) ? 4 : 3;
402
403 // Check coeffs
404 for(int k = 0; has_valid_coeffs && k < num_coeffs; k++)
405 if(!isnormal(img->wb_coeffs[k]) || img->wb_coeffs[k] == 0.0f) has_valid_coeffs = FALSE;
406
407 if(!has_valid_coeffs) return FALSE;
408
409 // Get white balance camera factors
410 dt_aligned_pixel_t WB = { img->wb_coeffs[0], img->wb_coeffs[1], img->wb_coeffs[2], img->wb_coeffs[3] };
411
412 // Adapt the camera coeffs with custom white balance if provided
413 // this can deal with WB coeffs that don't use the input matrix reference
414 if(custom_wb)
415 for(size_t k = 0; k < 4; k++) WB[k] *= custom_wb[k];
416
417 // Get the camera input profile (matrice of primaries)
418 float XYZ_to_CAM[4][3];
419 XYZ_to_CAM[0][0] = NAN;
420
421 if(!isnan(img->d65_color_matrix[0]))
422 {
423 // keep in sync with reload_defaults from colorin.c
424 // embedded matrix is used with higher priority than standard one
425 XYZ_to_CAM[0][0] = img->d65_color_matrix[0];
426 XYZ_to_CAM[0][1] = img->d65_color_matrix[1];
427 XYZ_to_CAM[0][2] = img->d65_color_matrix[2];
428
429 XYZ_to_CAM[1][0] = img->d65_color_matrix[3];
430 XYZ_to_CAM[1][1] = img->d65_color_matrix[4];
431 XYZ_to_CAM[1][2] = img->d65_color_matrix[5];
432
433 XYZ_to_CAM[2][0] = img->d65_color_matrix[6];
434 XYZ_to_CAM[2][1] = img->d65_color_matrix[7];
435 XYZ_to_CAM[2][2] = img->d65_color_matrix[8];
436 }
437 else
438 {
439 for(int k=0; k<4; k++)
440 for(int i=0; i<3; i++)
441 XYZ_to_CAM[k][i] = img->adobe_XYZ_to_CAM[k][i];
442 }
443
444 if(isnan(XYZ_to_CAM[0][0])) return FALSE;
445
446 // Bloody input matrices define XYZ -> CAM transform, as if we often needed camera profiles to output
447 // So we need to invert them. Here go your CPU cycles again.
448 float CAM_to_XYZ[4][3];
449 CAM_to_XYZ[0][0] = NAN;
450 matrice_pseudoinverse(XYZ_to_CAM, CAM_to_XYZ, 3);
451 if(isnan(CAM_to_XYZ[0][0])) return FALSE;
452
453 float x, y;
454 WB_coeffs_to_illuminant_xy(CAM_to_XYZ, WB, &x, &y);
455 *chroma_x = x;
456 *chroma_y = y;
457
458 return TRUE;
459}
460
461
462#ifdef _OPENMP
463#pragma omp declare simd
464#endif
465static inline float planckian_normal(const float x, const float t)
466{
467 float n = 0.f;
468
469 // Get the direction of the normal vector to the planckian locus at current temperature
470 // This is derivated from CCT_to_xy_blackbody equations
471 if(t >= 1667.f && t <= 2222.f)
472 n = (-3.3191442f * x - 2.69622040f) * x + 2.18555832f;
473 else if(t > 2222.f && t <= 4000.f)
474 n = (-2.8648428f * x - 2.74837186f) * x + 2.09137015f;
475 else if(t > 4000.f && t < 25000.f)
476 n = (9.2452740f * x - 11.7467734f) * x + 3.75112997f;
477 return n;
478}
479
480
481#ifdef _OPENMP
482#pragma omp declare simd
483#endif
484static inline void blackbody_xy_to_tinted_xy(const float x, const float y, const float t, const float tint,
485 float *x_out, float *y_out)
486{
487 // Move further away from planckian locus in the orthogonal direction, by an amount of "tint"
488 const float n = planckian_normal(x, t);
489 const float norm = sqrtf(1.f + n * n);
490 *x_out = x + tint * n / norm;
491 *y_out = y - tint / norm;
492}
493
494
495#ifdef _OPENMP
496#pragma omp declare simd
497#endif
498static inline float get_tint_from_tinted_xy(const float x, const float y, const float t)
499{
500 // Find the distance between planckian locus and arbitrary x y chromaticity in the orthogonal direction
501 const float n = planckian_normal(x, t);
502 const float norm = sqrtf(1.f + n * n);
503 float x_bb, y_bb;
504 CCT_to_xy_blackbody(t, &x_bb, &y_bb);
505 const float tint = -(y - y_bb) * norm;
506 return tint;
507}
508
509
510#ifdef _OPENMP
511#pragma omp declare simd
512#endif
513static inline void xy_to_uv(const float xy[2], float uv[2])
514{
515 // Convert to CIE1960 Yuv color space, usefull to compute CCT
516 // https://en.wikipedia.org/wiki/CIE_1960_color_space
517 const float denom = 12.f * xy[1] - 1.882f * xy[0] + 2.9088f;
518 uv[0] = 5.5932f * xy[0] + 1.9116 * xy[1];
519 uv[1] = 7.8972f * xy[1];
520 uv[0] /= denom;
521 uv[1] /= denom;
522}
523
524
525/*
526 * The following defines a custom OpenMP reduction so the reverse-lookup can be fully parallelized without sharing.
527 *
528 * Reference : https://stackoverflow.com/questions/61821090/parallelization-of-a-reverse-lookup-with-openmp
529 *
530 * The principle is that each thread has its own private radius and temperature, and find its own local minium radius.
531 * Then, at the end of the parallel loops, we fetch all the local minima from each thread, compare them and return
532 * the global minimum.
533 *
534 * This avoids sharing temperature and radius between threads and waiting for thread locks.
535 */
536typedef struct pair
537{
538 float radius;
541
542struct pair pair_min(struct pair r, struct pair n)
543{
544 // r is the current min value, n in the value to compare against it
545 if(n.radius < r.radius) return n;
546 else return r;
547}
548
549// Define a new reduction operation
550#ifdef _OPENMP
551#pragma omp declare reduction(pairmin:struct pair:omp_out=pair_min(omp_out,omp_in)) \
552 initializer(omp_priv = { FLT_MAX, 0.0f })
553#endif
554
555static inline float CCT_reverse_lookup(const float x, const float y)
556{
557 // Find out the closest correlated color temperature (closest point over the planckian locus)
558 // for any arbitrary x, y chromaticity, by brute-force reverse-lookup.
559 // Note that the LUT computation could be defered somewhere else, and computed once
560
561 static const float T_min = 1667.f;
562 static const float T_max = 25000.f;
563 static const float T_range = T_max - T_min;
564 static const size_t LUT_samples = 1<<16;
565
566 struct pair min_radius = { FLT_MAX, 0.0f };
567
568#if !(defined(__apple_build_version__) && __apple_build_version__ < 11030000) //makes Xcode 11.3.1 compiler crash
569#ifdef _OPENMP
570#pragma omp parallel for default(none) \
571 dt_omp_firstprivate(x, y, T_min, T_range, LUT_samples) reduction(pairmin:min_radius)\
572 schedule(simd:static)
573#endif
574#endif
575 for(size_t i = 0; i < LUT_samples; i++)
576 {
577 // we need more values for the low temperatures, so we scale the step with a power
578 const float step = powf((float)i / (float)(LUT_samples - 1), 4.0f);
579
580 // Current temperature in the lookup range
581 const float T = T_min + step * T_range;
582
583 // Current x, y chromaticity
584 float x_bb, y_bb;
585
586 if(T >= 4000.f)
587 CCT_to_xy_daylight(T, &x_bb, &y_bb);
588 else
589 CCT_to_xy_blackbody(T, &x_bb, &y_bb);
590
591 // Compute distance between current planckian chromaticity and input
592 const pair radius_tmp = { dt_fast_hypotf((x_bb - x), (y_bb - y)), T };
593
594 // If we found a smaller radius, save it
595 min_radius = pair_min(min_radius, radius_tmp);
596 }
597
598 return min_radius.temperature;
599}
600// clang-format off
601// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
602// vim: shiftwidth=2 expandtab tabstop=2 cindent
603// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
604// clang-format on
#define TRUE
Definition ashift_lsd.c:151
#define FALSE
Definition ashift_lsd.c:147
static float4 convert_XYZ_to_bradford_LMS(const float4 XYZ)
Definition colorspace.h:639
static void bradford_adapt_D50(float4 *lms_in, const float4 origin_illuminant, const float p, const int full)
Definition colorspace.h:679
static float4 convert_bradford_LMS_to_XYZ(const float4 LMS)
Definition colorspace.h:649
static void dt_XYZ_to_Rec709_D50(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
Definition colorspaces_inline_conversions.h:400
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
Definition common/image.c:185
@ DT_IMAGE_4BAYER
Definition common/image.h:81
#define DT_ALIGNED_ARRAY
Definition darktable.h:270
static int illuminant_to_xy(const dt_illuminant_t illuminant, const dt_image_t *img, const dt_aligned_pixel_t custom_wb, float *x_out, float *y_out, const float t, const dt_illuminant_fluo_t fluo, const dt_illuminant_led_t iled)
Definition illuminants.h:229
dt_illuminant_t
Definition illuminants.h:27
@ DT_ILLUMINANT_A
Definition illuminants.h:29
@ DT_ILLUMINANT_PIPE
Definition illuminants.h:28
@ DT_ILLUMINANT_CAMERA
Definition illuminants.h:38
@ DT_ILLUMINANT_BB
Definition illuminants.h:34
@ DT_ILLUMINANT_F
Definition illuminants.h:32
@ DT_ILLUMINANT_CUSTOM
Definition illuminants.h:35
@ DT_ILLUMINANT_LED
Definition illuminants.h:33
@ DT_ILLUMINANT_DETECT_EDGES
Definition illuminants.h:37
@ DT_ILLUMINANT_E
Definition illuminants.h:31
@ DT_ILLUMINANT_LAST
Definition illuminants.h:39
@ DT_ILLUMINANT_DETECT_SURFACES
Definition illuminants.h:36
@ DT_ILLUMINANT_D
Definition illuminants.h:30
static void WB_coeffs_to_illuminant_xy(const float CAM_to_XYZ[4][3], const dt_aligned_pixel_t WB, float *x, float *y)
Definition illuminants.h:330
static void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
Definition illuminants.h:212
static void illuminant_xy_to_RGB(const float x, const float y, dt_aligned_pixel_t RGB)
Definition illuminants.h:193
static void illuminant_xy_to_XYZ(const float x, const float y, dt_aligned_pixel_t XYZ)
Definition illuminants.h:182
static float xy_to_CCT(const float x, const float y)
Definition illuminants.h:121
static void CCT_to_xy_daylight(const float t, float *x, float *y)
Definition illuminants.h:135
dt_illuminant_led_t
Definition illuminants.h:62
@ DT_ILLUMINANT_LED_BH1
Definition illuminants.h:68
@ DT_ILLUMINANT_LED_B5
Definition illuminants.h:67
@ DT_ILLUMINANT_LED_B2
Definition illuminants.h:64
@ DT_ILLUMINANT_LED_B1
Definition illuminants.h:63
@ DT_ILLUMINANT_LED_LAST
Definition illuminants.h:72
@ DT_ILLUMINANT_LED_V1
Definition illuminants.h:70
@ DT_ILLUMINANT_LED_B3
Definition illuminants.h:65
@ DT_ILLUMINANT_LED_B4
Definition illuminants.h:66
@ DT_ILLUMINANT_LED_V2
Definition illuminants.h:71
@ DT_ILLUMINANT_LED_RGB1
Definition illuminants.h:69
static float CCT_reverse_lookup(const float x, const float y)
Definition illuminants.h:555
static float get_tint_from_tinted_xy(const float x, const float y, const float t)
Definition illuminants.h:498
static float planckian_normal(const float x, const float t)
Definition illuminants.h:465
static int find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb, float *chroma_x, float *chroma_y)
Definition illuminants.h:394
static void CCT_to_xy_blackbody(const float t, float *x, float *y)
Definition illuminants.h:156
static float fluorescent[DT_ILLUMINANT_FLUO_LAST][2]
Definition illuminants.h:86
static void matrice_pseudoinverse(float(*in)[3], float(*out)[3], int size)
Definition illuminants.h:359
dt_illuminant_fluo_t
Definition illuminants.h:44
@ DT_ILLUMINANT_FLUO_F6
Definition illuminants.h:50
@ DT_ILLUMINANT_FLUO_F4
Definition illuminants.h:48
@ DT_ILLUMINANT_FLUO_F1
Definition illuminants.h:45
@ DT_ILLUMINANT_FLUO_F5
Definition illuminants.h:49
@ DT_ILLUMINANT_FLUO_F2
Definition illuminants.h:46
@ DT_ILLUMINANT_FLUO_F3
Definition illuminants.h:47
@ DT_ILLUMINANT_FLUO_F12
Definition illuminants.h:56
@ DT_ILLUMINANT_FLUO_F8
Definition illuminants.h:52
@ DT_ILLUMINANT_FLUO_F9
Definition illuminants.h:53
@ DT_ILLUMINANT_FLUO_LAST
Definition illuminants.h:57
@ DT_ILLUMINANT_FLUO_F11
Definition illuminants.h:55
@ DT_ILLUMINANT_FLUO_F7
Definition illuminants.h:51
@ DT_ILLUMINANT_FLUO_F10
Definition illuminants.h:54
static void blackbody_xy_to_tinted_xy(const float x, const float y, const float t, const float tint, float *x_out, float *y_out)
Definition illuminants.h:484
static void xy_to_uv(const float xy[2], float uv[2])
Definition illuminants.h:513
static float led[DT_ILLUMINANT_LED_LAST][2]
Definition illuminants.h:108
struct pair pair_min(struct pair r, struct pair n)
Definition illuminants.h:542
static float dt_fast_hypotf(const float x, const float y)
Definition math.h:272
size_t size
Definition mipmap_cache.c:3
Definition common/image.h:195
int32_t flags
Definition common/image.h:227
float d65_color_matrix[9]
Definition common/image.h:236
float adobe_XYZ_to_CAM[4][3]
Definition common/image.h:259
dt_aligned_pixel_t wb_coeffs
Definition common/image.h:256
Definition illuminants.h:537
float temperature
Definition illuminants.h:539
float radius
Definition illuminants.h:538