Skip to content
Open
123 changes: 82 additions & 41 deletions src/common/illuminants.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef enum dt_illuminant_t
DT_ILLUMINANT_BB = 6, // $DESCRIPTION: "Planckian (black body)" general black body radiator - not CIE standard
DT_ILLUMINANT_CUSTOM = 7, // $DESCRIPTION: "custom" input x and y directly - bypass search
DT_ILLUMINANT_CAMERA = 10,// $DESCRIPTION: "as shot in camera" read RAW EXIF for WB
DT_ILLUMINANT_FROM_WB = 11,// $DESCRIPTION: "as set in white balance module" read coefficients from the white balance module
DT_ILLUMINANT_LAST,
DT_ILLUMINANT_DETECT_SURFACES = 8,
DT_ILLUMINANT_DETECT_EDGES = 9,
Expand Down Expand Up @@ -215,19 +216,22 @@ static inline void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
illuminant_xy_to_RGB(x, y, RGB);
}

static inline gboolean find_temperature_from_wb_coeffs(const dt_image_t *img, const dt_aligned_pixel_t wb_coeffs,
float *chroma_x, float *chroma_y);

// Fetch image from pipeline and read EXIF for camera RAW WB coeffs
static inline gboolean find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb,
static inline gboolean find_temperature_from_as_shot_coeffs(const dt_image_t *img, const dt_aligned_pixel_t correction_ratios,
float *chroma_x, float *chroma_y);


static inline int illuminant_to_xy(const dt_illuminant_t illuminant, // primary type of illuminant
const dt_image_t *img, // image container
const dt_aligned_pixel_t custom_wb, // optional user-set WB coeffs
float *x_out, float *y_out, // chromaticity output
const float t, // temperature in K, if needed
const dt_illuminant_fluo_t fluo, // sub-type of fluorescent illuminant, if needed
const dt_illuminant_led_t iled) // sub-type of led illuminant, if needed
static inline int illuminant_to_xy(const dt_illuminant_t illuminant, // primary type of illuminant
const dt_image_t *img, // image container
const dt_aligned_pixel_t correction_ratios, // optional D65 correction ratios derived from user-set coefficients
const dt_aligned_pixel_t wb_coeffs, // optional user-set WB coeffs (absolute)
float *x_out, float *y_out, // chromaticity output
const float t, // temperature in K, if needed
const dt_illuminant_fluo_t fluo, // sub-type of fluorescent illuminant, if needed
const dt_illuminant_led_t iled) // sub-type of led illuminant, if needed
{
/**
* Compute the x and y chromaticity coordinates in Yxy spaces for standard illuminants
Expand Down Expand Up @@ -294,13 +298,26 @@ static inline int illuminant_to_xy(const dt_illuminant_t illuminant, // primary
// Model valid for T in [1667 ; 25000] K
CCT_to_xy_blackbody(t, &x, &y);
if(y != 0.f && x != 0.f) break;
// else t is out of bounds -> use custom/original values (next case)
// else t is out of bounds -> use custom/original values
return FALSE;
}
case DT_ILLUMINANT_CAMERA:
{
// Detect WB from RAW EXIF
if(img)
if(find_temperature_from_raw_coeffs(img, custom_wb, &x, &y)) break;
// Detect WB from RAW EXIF, correcting with D65/wb_coeff ratios
if(img && find_temperature_from_as_shot_coeffs(img, correction_ratios, &x, &y))
break;

// xy calculation failed
return FALSE;
}
case DT_ILLUMINANT_FROM_WB:
{
// Detect WB from user-provided coefficients
if(img && find_temperature_from_wb_coeffs(img, wb_coeffs, &x, &y))
break;

// xy calculation failed
return FALSE;
}
case DT_ILLUMINANT_CUSTOM: // leave x and y as-is
case DT_ILLUMINANT_DETECT_EDGES:
Expand Down Expand Up @@ -385,31 +402,11 @@ static inline void matrice_pseudoinverse(float (*in)[3], float (*out)[3], int si
}
}


static gboolean find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb,
float *chroma_x, float *chroma_y)
// returns TRUE if OK, FALSE if failed
static gboolean get_CAM_to_XYZ(const dt_image_t * img, float(* CAM_to_XYZ)[3])
{
if(img == NULL) return FALSE;
if(!dt_image_is_matrix_correction_supported(img)) return FALSE;

gboolean has_valid_coeffs = TRUE;
const int num_coeffs = (img->flags & DT_IMAGE_4BAYER) ? 4 : 3;

// Check coeffs
for(int k = 0; has_valid_coeffs && k < num_coeffs; k++)
if(!dt_isnormal(img->wb_coeffs[k]) || img->wb_coeffs[k] == 0.0f) has_valid_coeffs = FALSE;

if(!has_valid_coeffs) return FALSE;

// Get white balance camera factors
dt_aligned_pixel_t WB = { img->wb_coeffs[0], img->wb_coeffs[1], img->wb_coeffs[2], img->wb_coeffs[3] };

// Adapt the camera coeffs with custom white balance if provided
// this can deal with WB coeffs that don't use the input matrix reference
if(custom_wb)
for(size_t k = 0; k < 4; k++) WB[k] *= custom_wb[k];

// Get the camera input profile (matrice of primaries)
if(img == NULL || CAM_to_XYZ == NULL) return FALSE;
// Get the camera input profile (matrice of primaries) - embedded or raw library DB
float XYZ_to_CAM[4][3];
dt_mark_colormatrix_invalid(&XYZ_to_CAM[0][0]);

Expand Down Expand Up @@ -438,21 +435,65 @@ static gboolean find_temperature_from_raw_coeffs(const dt_image_t *img, const dt

if(!dt_is_valid_colormatrix(XYZ_to_CAM[0][0])) return FALSE;

// Bloody input matrices define XYZ -> CAM transform, as if we often needed camera profiles to output
// So we need to invert them. Here go your CPU cycles again.
float CAM_to_XYZ[4][3];
dt_mark_colormatrix_invalid(&CAM_to_XYZ[0][0]);
matrice_pseudoinverse(XYZ_to_CAM, CAM_to_XYZ, 3);
if(!dt_is_valid_colormatrix(CAM_to_XYZ[0][0])) return FALSE;
return dt_is_valid_colormatrix(CAM_to_XYZ[0][0]);
}

static inline gboolean _wb_coeffs_invalid(const dt_aligned_pixel_t wb_coeffs, const int num_coeffs)
{
for(int k = 0; k < num_coeffs; k++)
if(!dt_isnormal(wb_coeffs[k]) || wb_coeffs[k] == 0.0f) return TRUE;

return FALSE;
}

// returns FALSE if failed; TRUE if successful
static gboolean find_temperature_from_wb_coeffs(const dt_image_t *img, const dt_aligned_pixel_t wb_coeffs,
float *chroma_x, float *chroma_y)
{
if(img == NULL || wb_coeffs == NULL) return FALSE;
if(!dt_image_is_matrix_correction_supported(img)) return FALSE;
// it's enough to check the first 3 here, as WB_coeffs_to_illuminant_xy only uses indices 0 to 2
if(_wb_coeffs_invalid(wb_coeffs, 3)) return FALSE;

float CAM_to_XYZ[4][3];
if(!get_CAM_to_XYZ(img, CAM_to_XYZ)) {
return FALSE;
}

float x, y;
WB_coeffs_to_illuminant_xy(CAM_to_XYZ, WB, &x, &y);
WB_coeffs_to_illuminant_xy(CAM_to_XYZ, wb_coeffs, &x, &y);
*chroma_x = x;
*chroma_y = y;

return TRUE;
}

// returns FALSE if failed; TRUE if successful
static gboolean find_temperature_from_as_shot_coeffs(const dt_image_t *img, const dt_aligned_pixel_t correction_ratios,
float *chroma_x, float *chroma_y)
{
if(img == NULL) return FALSE;
const int num_coeffs = (img->flags & DT_IMAGE_4BAYER) ? 4 : 3;

if(_wb_coeffs_invalid(img->wb_coeffs, num_coeffs)) return FALSE;

// Get as-shot white balance camera factors (from raw)
// component wise raw-RGB * wb_coeffs should provide R=G=B for a neutral patch under
// the scene illuminant
dt_aligned_pixel_t WB = { img->wb_coeffs[0], img->wb_coeffs[1], img->wb_coeffs[2], img->wb_coeffs[3] };

// Adapt the camera coeffs with custom D65 coefficients if provided ('caveats' workaround)
// this can deal with WB coeffs that don't use the input matrix reference
// correction_ratios[k] = chr->D65coeffs[k] / chr->wb_coeffs[k]
if(correction_ratios)
for(size_t k = 0; k < 4; k++) WB[k] *= correction_ratios[k];
// for a neutral surface, raw RGB * img->wb_coeffs would produce neutral R=G=B

return find_temperature_from_wb_coeffs(img, WB, chroma_x, chroma_y);
}


DT_OMP_DECLARE_SIMD()
static inline float planckian_normal(const float x, const float t)
Expand Down
6 changes: 3 additions & 3 deletions src/develop/develop.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ typedef struct dt_dev_viewport_t
- the currently used wb_coeffs in temperature module
- D65coeffs and as_shot are read from exif data
f) - late_correction set by temperature if we want to process data as following
If we use the new DT_IOP_TEMP_D65_LATE mode in temperature.c and don#t have
any temp parameters changes later we can calc correction coeffs to modify
as_shot rgb data to D65
If we use the new DT_IOP_TEMP_D65_LATE mode or enable this in user modes of temperature.c
and don't have any temp parameters changes later we can calc correction ratios
to take white-balanced (neutralized) data to D65
*/
typedef struct dt_dev_chroma_t
{
Expand Down
Loading
Loading