Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 83 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,23 @@ static inline void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
illuminant_xy_to_RGB(x, y, RGB);
}

// Derive illuminant chromaticity from WB module coefficients
static inline gboolean find_illuminant_xy_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_illuminant_xy_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 +299,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_illuminant_xy_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_illuminant_xy_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 +403,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 +436,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_illuminant_xy_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;

Comment thread
kofa73 marked this conversation as resolved.
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_illuminant_xy_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_illuminant_xy_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