@@ -34,6 +34,7 @@ typedef enum dt_illuminant_t
3434 DT_ILLUMINANT_BB = 6 , // $DESCRIPTION: "Planckian (black body)" general black body radiator - not CIE standard
3535 DT_ILLUMINANT_CUSTOM = 7 , // $DESCRIPTION: "custom" input x and y directly - bypass search
3636 DT_ILLUMINANT_CAMERA = 10 ,// $DESCRIPTION: "as shot in camera" read RAW EXIF for WB
37+ DT_ILLUMINANT_FROM_WB = 11 ,// $DESCRIPTION: "as set in white balance module" read coefficients from the white balance module
3738 DT_ILLUMINANT_LAST ,
3839 DT_ILLUMINANT_DETECT_SURFACES = 8 ,
3940 DT_ILLUMINANT_DETECT_EDGES = 9 ,
@@ -215,19 +216,22 @@ static inline void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
215216 illuminant_xy_to_RGB (x , y , RGB );
216217}
217218
219+ static inline gboolean find_temperature_from_wb_coeffs (const dt_image_t * img , const dt_aligned_pixel_t wb_coeffs ,
220+ float * chroma_x , float * chroma_y );
218221
219222// Fetch image from pipeline and read EXIF for camera RAW WB coeffs
220- static inline gboolean find_temperature_from_raw_coeffs (const dt_image_t * img , const dt_aligned_pixel_t custom_wb ,
223+ static inline gboolean find_temperature_from_as_shot_coeffs (const dt_image_t * img , const dt_aligned_pixel_t correction_ratios ,
221224 float * chroma_x , float * chroma_y );
222225
223226
224- static inline int illuminant_to_xy (const dt_illuminant_t illuminant , // primary type of illuminant
225- const dt_image_t * img , // image container
226- const dt_aligned_pixel_t custom_wb , // optional user-set WB coeffs
227- float * x_out , float * y_out , // chromaticity output
228- const float t , // temperature in K, if needed
229- const dt_illuminant_fluo_t fluo , // sub-type of fluorescent illuminant, if needed
230- const dt_illuminant_led_t iled ) // sub-type of led illuminant, if needed
227+ static inline int illuminant_to_xy (const dt_illuminant_t illuminant , // primary type of illuminant
228+ const dt_image_t * img , // image container
229+ const dt_aligned_pixel_t correction_ratios , // optional D65 correction ratios derived from user-set coefficients
230+ const dt_aligned_pixel_t wb_coeffs , // optional user-set WB coeffs (absolute)
231+ float * x_out , float * y_out , // chromaticity output
232+ const float t , // temperature in K, if needed
233+ const dt_illuminant_fluo_t fluo , // sub-type of fluorescent illuminant, if needed
234+ const dt_illuminant_led_t iled ) // sub-type of led illuminant, if needed
231235{
232236 /**
233237 * Compute the x and y chromaticity coordinates in Yxy spaces for standard illuminants
@@ -298,9 +302,15 @@ static inline int illuminant_to_xy(const dt_illuminant_t illuminant, // primary
298302 }
299303 case DT_ILLUMINANT_CAMERA :
300304 {
301- // Detect WB from RAW EXIF
305+ // Detect WB from RAW EXIF, correcting with D65/wb_coeff ratios
302306 if (img )
303- if (find_temperature_from_raw_coeffs (img , custom_wb , & x , & y )) break ;
307+ if (find_temperature_from_as_shot_coeffs (img , correction_ratios , & x , & y )) break ;
308+ }
309+ case DT_ILLUMINANT_FROM_WB :
310+ {
311+ // Detect WB from user-provided coefficients
312+ if (img )
313+ if (find_temperature_from_wb_coeffs (img , wb_coeffs , & x , & y )) break ;
304314 }
305315 case DT_ILLUMINANT_CUSTOM : // leave x and y as-is
306316 case DT_ILLUMINANT_DETECT_EDGES :
@@ -385,31 +395,11 @@ static inline void matrice_pseudoinverse(float (*in)[3], float (*out)[3], int si
385395 }
386396}
387397
388-
389- static gboolean find_temperature_from_raw_coeffs (const dt_image_t * img , const dt_aligned_pixel_t custom_wb ,
390- float * chroma_x , float * chroma_y )
398+ // returns TRUE is OK, FALSE if failed
399+ static gboolean get_CAM_to_XYZ (const dt_image_t * img , float (* CAM_to_XYZ )[3 ])
391400{
392- if (img == NULL ) return FALSE;
393- if (!dt_image_is_matrix_correction_supported (img )) return FALSE;
394-
395- gboolean has_valid_coeffs = TRUE;
396- const int num_coeffs = (img -> flags & DT_IMAGE_4BAYER ) ? 4 : 3 ;
397-
398- // Check coeffs
399- for (int k = 0 ; has_valid_coeffs && k < num_coeffs ; k ++ )
400- if (!dt_isnormal (img -> wb_coeffs [k ]) || img -> wb_coeffs [k ] == 0.0f ) has_valid_coeffs = FALSE;
401-
402- if (!has_valid_coeffs ) return FALSE;
403-
404- // Get white balance camera factors
405- dt_aligned_pixel_t WB = { img -> wb_coeffs [0 ], img -> wb_coeffs [1 ], img -> wb_coeffs [2 ], img -> wb_coeffs [3 ] };
406-
407- // Adapt the camera coeffs with custom white balance if provided
408- // this can deal with WB coeffs that don't use the input matrix reference
409- if (custom_wb )
410- for (size_t k = 0 ; k < 4 ; k ++ ) WB [k ] *= custom_wb [k ];
411-
412- // Get the camera input profile (matrice of primaries)
401+ if (img == NULL || CAM_to_XYZ == NULL ) return FALSE;
402+ // Get the camera input profile (matrice of primaries) - embedded or raw library DB
413403 float XYZ_to_CAM [4 ][3 ];
414404 dt_mark_colormatrix_invalid (& XYZ_to_CAM [0 ][0 ]);
415405
@@ -438,21 +428,61 @@ static gboolean find_temperature_from_raw_coeffs(const dt_image_t *img, const dt
438428
439429 if (!dt_is_valid_colormatrix (XYZ_to_CAM [0 ][0 ])) return FALSE;
440430
441- // Bloody input matrices define XYZ -> CAM transform, as if we often needed camera profiles to output
442- // So we need to invert them. Here go your CPU cycles again.
443- float CAM_to_XYZ [4 ][3 ];
444431 dt_mark_colormatrix_invalid (& CAM_to_XYZ [0 ][0 ]);
445432 matrice_pseudoinverse (XYZ_to_CAM , CAM_to_XYZ , 3 );
446- if (!dt_is_valid_colormatrix (CAM_to_XYZ [0 ][0 ])) return FALSE;
433+ return dt_is_valid_colormatrix (CAM_to_XYZ [0 ][0 ]);
434+ }
435+
436+ // returns FALSE if failed; TRUE if successful
437+ static gboolean find_temperature_from_wb_coeffs (const dt_image_t * img , const dt_aligned_pixel_t wb_coeffs ,
438+ float * chroma_x , float * chroma_y )
439+ {
440+ if (img == NULL || wb_coeffs == NULL ) return FALSE;
441+ if (!dt_image_is_matrix_correction_supported (img )) return FALSE;
442+
443+ float CAM_to_XYZ [4 ][3 ];
444+ if (!get_CAM_to_XYZ (img , CAM_to_XYZ )) {
445+ return FALSE;
446+ }
447447
448448 float x , y ;
449- WB_coeffs_to_illuminant_xy (CAM_to_XYZ , WB , & x , & y );
449+ WB_coeffs_to_illuminant_xy (CAM_to_XYZ , wb_coeffs , & x , & y );
450450 * chroma_x = x ;
451451 * chroma_y = y ;
452452
453453 return TRUE;
454454}
455455
456+ // returns FALSE if failed; TRUE if successful
457+ static gboolean find_temperature_from_as_shot_coeffs (const dt_image_t * img , const dt_aligned_pixel_t correction_ratios ,
458+ float * chroma_x , float * chroma_y )
459+ {
460+ if (img == NULL ) return FALSE;
461+
462+ gboolean has_valid_coeffs = TRUE;
463+ const int num_coeffs = (img -> flags & DT_IMAGE_4BAYER ) ? 4 : 3 ;
464+
465+ // Check coeffs
466+ for (int k = 0 ; has_valid_coeffs && k < num_coeffs ; k ++ )
467+ if (!dt_isnormal (img -> wb_coeffs [k ]) || img -> wb_coeffs [k ] == 0.0f ) has_valid_coeffs = FALSE;
468+
469+ if (!has_valid_coeffs ) return FALSE;
470+
471+ // Get as-shot white balance camera factors (from raw)
472+ // component wise raw-RGB * wb_coeffs should provide R=G=B for a neutral patch under
473+ // the scene illuminant
474+ dt_aligned_pixel_t WB = { img -> wb_coeffs [0 ], img -> wb_coeffs [1 ], img -> wb_coeffs [2 ], img -> wb_coeffs [3 ] };
475+
476+ // Adapt the camera coeffs with custom D65 coefficients if provided ('caveats' workaround)
477+ // this can deal with WB coeffs that don't use the input matrix reference
478+ // adaptation_ratios[k] = chr->D65coeffs[k] / chr->wb_coeffs[k]
479+ if (correction_ratios )
480+ for (size_t k = 0 ; k < 4 ; k ++ ) WB [k ] *= correction_ratios [k ];
481+ // for a neutral surface, raw RGB * img->wb_coeffs would produce neutral R=G=B
482+
483+ return find_temperature_from_wb_coeffs (img , WB , chroma_x , chroma_y );
484+ }
485+
456486
457487DT_OMP_DECLARE_SIMD ()
458488static inline float planckian_normal (const float x , const float t )
0 commit comments