in src/open_vp_cal/core/calibrate.py [0:0]
def run(
measured_samples: Dict,
reference_samples: Dict,
input_plate_gamut: Union[str, RGB_Colourspace, constants.ColourSpace],
native_camera_gamut: Union[str, RGB_Colourspace, constants.CameraColourSpace],
target_gamut: Union[str, RGB_Colourspace, constants.ColourSpace],
target_to_screen_cat: Union[str, CAT],
reference_to_target_cat: Union[str, CAT],
target_max_lum_nits: int,
target_EOTF: Union[None, str, EOTF] = None,
enable_plate_white_balance: bool = True,
enable_gamut_compression: bool = True,
enable_EOTF_correction: bool = True,
calculation_order: Union[str, CalculationOrder] = CalculationOrder.CO_DEFAULT,
gamut_compression_shadow_rolloff: float = constants.GAMUT_COMPRESSION_SHADOW_ROLLOFF,
reference_wall_external_white_balance_matrix: Union[None, List] = None,
decoupled_lens_white_samples: Union[None, List] = None,
avoid_clipping: bool = True,
reference_gamut = constants.ColourSpace.CS_ACES
):
""" Run the entire calibration process.
Args:
measured_samples: a dictionary containing the measured values sampled from the input plate
reference_samples: a dictionary containing the reference values for the calibration process displayed on
the led wall
input_plate_gamut: The colour space of the input plate we measured the samples from, this should be ACES2065-1,
the samples and the plate are preprocessed in the framework into ACES before being passed in.
If using directly ensure your samples are pre processed correctly
native_camera_gamut: The native colour space of the camera, used to capture the input plate
target_gamut: The colour space we want to target for the calibration
target_to_screen_cat: The chromatic adaptation transform method for target to screen calibration matrix
target_max_lum_nits: The maximum luminance of the target led wall expressed in nits
target_EOTF: The name of the EOTF we want to target for the calibration
enable_plate_white_balance: Applies the white balance matrix calculated for the input plate
enable_gamut_compression: Whether to enable the gamut compression or not, as part of the calibration
enable_EOTF_correction: Whether to enable the EOTF correction or now, as part of the calibration
calculation_order: The order each step of the calibration is calculated and applied
gamut_compression_shadow_rolloff: A rolloff to apply to dark colours
reference_to_target_cat: The chromatic adaptation transform method, for reference to target matrix
reference_wall_external_white_balance_matrix: A precomputed white balance matrix to apply to the input plate
samples, used when matching other led walls. By default, the white balance matrix is independently
calculated for each wall
Returns: A dictionary containing the results of the calibration process in a json serializable format
PRE_CALIBRATION_SCREEN_PRIMARIES: (List) The calculated screen colour space primaries CIE1931-xy,
pre calibration
PRE_CALIBRATION_SCREEN_WHITEPOINT: (List) The calculated screen colour space white point CIE1931-xy,
pre calibration
TARGET_GAMUT: The name of the target gamut used for the calibration,
ENABLE_PLATE_WHITE_BALANCE: (bool) Whether the plate white balance was enabled or not
ENABLE_GAMUT_COMPRESSION: (bool) Whether the gamut compression was enabled or not
ENABLE_EOTF_CORRECTION: (bool) Whether the EOTF correction was enabled or not
CALCULATION_ORDER: (str) The order each step of the calibration is calculated and applied
WHITE_BALANCE_MATRIX: (List) The white balance matrix applied to the plate samples
TARGET_TO_SCREEN_MATRIX: (List) The calculated target to screen calibration matrix
REFERENCE_TO_SCREEN_MATRIX: (List) The calculated reference to screen calibration matrix
REFERENCE_TO_TARGET_MATRIX: (List) The calculated reference to target matrix
EOTF_LUT_R: (List) The values for the red channel of the EOTF correction LUT
EOTF_LUT_G: (List) The values for the green channel of the EOTF correction LUT
EOTF_LUT_B: (List) The values for the blue channel of the EOTF correction LUT
MAX_DISTANCES: (List) The calculated maximum distances for the out of gamut colours, from the target gamut
TARGET_EOTF: The name of the EOTF we want to target for the calibration
NATIVE_CAMERA_GAMUT: The native colour space of the camera, used to capture the input plate
OCIO_REFERENCE_GAMUT: The reference colour space of the ocio config we will be writing too
POST_CALIBRATION_SCREEN_PRIMARIES: (List) The calculated screen colour space primaries CIE1931-xy, post calibration
POST_CALIBRATION_SCREEN_WHITEPOINT: (List) The calculated screen colour space white point CIE1931-xy, post calibration
PRE_EOTF_RAMPS: The measured EOTF of the led wall based on the grey ramp tracking
POST_EOTF_RAMPS: The measured EOTF of the led wall, with the EOTF correction applied
DELTA_E_RGBW: The IPT DeltaE of the R, G, B, W between the measured and reference samples
DELTA_E_EOTF_RAMP: The IPT DeltaE of the EOTF Ramp between the measured and reference samples
DELTA_E_MACBETH: The IPT DeltaE of the Macbeth chart between the measured and reference samples
EXPOSURE_SCALING_FACTOR: Normalization scaling factor for the measured samples
TARGET_MAX_LUM_NITS: The maximum luminance of the target led wall expressed in nits
MEASURED_18_PERCENT_SAMPLE: The measured 18 percent sample as seen through the camera, using the green channel
MEASURED_MAX_LUM_NITS: The measured maximum luminance of the led wall expressed in nits
REFERENCE_EOTF_RAMP: The reference EOTF ramp values
TARGET_TO_XYZ_MATRIX: The computed target colour space to XYZ matrix
REFERENCE_TO_XYZ_MATRIX: The computed reference colour space to XYZ matrix
REFERENCE_TO_INPUT_MATRIX: The computed reference to input plate colour space matrix
"""
# If we are not working in PQ we force target nits to 100 aka 1.0
if target_EOTF != constants.EOTF.EOTF_ST2084:
target_max_lum_nits = 100
peak_lum = target_max_lum_nits * 0.01
if target_to_screen_cat == constants.CAT.CAT_NONE:
target_to_screen_cat = None
# 0) Ensure we can do only one of auto white balance, external white balance, or decoupled lens white balance
configuration_check = [
enable_plate_white_balance,
reference_wall_external_white_balance_matrix is not None,
decoupled_lens_white_samples is not None].count(True)
if configuration_check > 1:
raise OpenVPCalException("Only one of auto white balance, external white balance, "
"or decoupled lens white balance is allowed")
# 0) Validate that the EOTF ramp for the last frame is not accidentally the
# end slate due to infra structure issues
check_eotf_max_values(measured_samples)
# 1) First We Get Our Colour Spaces
input_plate_cs, native_camera_gamut_cs, target_cs, reference_cs = get_calibration_colour_spaces(
input_plate_gamut, native_camera_gamut, target_gamut, reference_gamut)
# 1a) If our input plate gamut is different to our native camera gamut,
# we convert the samples to reference gamut, if samples are coming from the framework
# samples will already be in reference gamut and we skip this step
if input_plate_gamut != reference_gamut:
for key in measured_samples:
if key not in [constants.Measurements.EOTF_RAMP_SIGNAL, constants.Measurements.PRIMARIES_SATURATION]:
measured_samples[key] = colour.RGB_to_RGB(
measured_samples[key], input_plate_cs, reference_cs,
None
).tolist()
# Now we convert our samples from input to reference out input_plate_cs is now
# the reference_cs
input_plate_cs = reference_cs
# 2) Once we have our camera native colour space we decide on the cat we want to use to convert to camera space
camera_conversion_cat = utils.get_cat_for_camera_conversion(native_camera_gamut_cs.name)
# 3) We take our measured samples and convert them to camera space
(
eotf_ramp_camera_native_gamut, grey_measurements_native_camera_gamut,
macbeth_measurements_camera_native_gamut, max_white_camera_native_gamut,
rgbw_measurements_camera_native_gamut,
eotf_signal_values, decoupled_lens_white_samples_camera_native_gamut,
rgbw_reference_samples, macbeth_reference_samples, eotf_ramp_reference_samples
) = convert_samples_to_required_cs(
camera_conversion_cat, input_plate_cs, measured_samples, reference_samples, native_camera_gamut_cs,
decoupled_lens_white_samples, peak_lum
)
# 4) We Calculate a decoupled white balance matrix if we have a decoupled lens white samples
white_balance_matrix = np.identity(3)
if decoupled_lens_white_samples_camera_native_gamut is not None:
white_balance_matrix = create_decoupling_white_balance_matrix(
grey_measurements_native_camera_gamut, decoupled_lens_white_samples_camera_native_gamut)
# 4) We Calculate The White Balance Matrix By Balancing The Grey Samples Against The Green Channel Or Use The One
if reference_wall_external_white_balance_matrix is None and enable_plate_white_balance:
white_balance_matrix = utils.create_white_balance_matrix(grey_measurements_native_camera_gamut)
if reference_wall_external_white_balance_matrix:
white_balance_matrix = np.array(reference_wall_external_white_balance_matrix)
# 5) We Apply The White Balance Matrix To All The Samples
(
eotf_ramp_camera_native_gamut, grey_measurements_native_camera_gamut,
macbeth_measurements_camera_native_gamut, max_white_camera_native_gamut,
rgbw_measurements_camera_native_gamut
) = apply_matrix_to_samples(
white_balance_matrix, eotf_ramp_camera_native_gamut,
grey_measurements_native_camera_gamut,
macbeth_measurements_camera_native_gamut, max_white_camera_native_gamut,
rgbw_measurements_camera_native_gamut
)
# 6) We Get The Matrix To Convert From OCIO Reference Space To Target Space
(
reference_to_target_matrix, ocio_reference_cs,
target_to_XYZ_matrix, reference_to_XYZ_matrix,
reference_to_input_matrix
) = get_ocio_reference_to_target_matrix(
input_plate_cs, target_cs, reference_gamut, cs_cat=reference_to_target_cat
)
# 7) We Get The Max Green Value Of The EOTF Ramp And We Scale So It Hits Peak Lum
grey_measurements_white_balanced_native_gamut = rgbw_measurements_camera_native_gamut[3]
grey_measurements_white_balanced_native_gamut_green = grey_measurements_white_balanced_native_gamut[1]
green_value_for_last_eotf_patch = eotf_ramp_camera_native_gamut[-1][1]
target_over_white = 1 / green_value_for_last_eotf_patch
exposure_scaling_factor = 1.0 / (peak_lum * target_over_white)
max_white_delta = max_white_camera_native_gamut[1] / eotf_ramp_camera_native_gamut[-1][1]
rgbw_measurements_camera_native_gamut = rgbw_measurements_camera_native_gamut / exposure_scaling_factor
eotf_ramp_camera_native_gamut = [
item / exposure_scaling_factor for item in eotf_ramp_camera_native_gamut
]
macbeth_measurements_camera_native_gamut = [
item / exposure_scaling_factor for item in macbeth_measurements_camera_native_gamut
]
# 8) We do the deltaE analysis
delta_e_wrgb, delta_e_eotf_ramp, delta_e_macbeth = deltaE_ICtCp(
rgbw_reference_samples, macbeth_reference_samples, eotf_ramp_reference_samples,
rgbw_measurements_camera_native_gamut, eotf_ramp_camera_native_gamut,
macbeth_measurements_camera_native_gamut, target_cs, native_camera_gamut_cs
)
# 9 If we have disabled eotf correction, we have to force the operation order
if not enable_EOTF_correction:
calculation_order = CalculationOrder.CO_CS_EOTF
eotf_signal_value_rgb = np.array([
[signal, signal, signal] for signal in eotf_signal_values
])
# 10 We calculate the difference in the linearity of the wall based on the signals and eotf ramps
eotf_linearity = calculate_eotf_linearity(eotf_signal_values, eotf_ramp_camera_native_gamut)
eotf_ramp_camera_native_gamut_calibrated = eotf_ramp_camera_native_gamut.copy()
macbeth_measurements_camera_native_gamut_calibrated = macbeth_measurements_camera_native_gamut.copy()
if calculation_order == CalculationOrder.CO_CS_EOTF: # Calc 3x3 -> 1Ds, and 3x3 only
# 2: Create target to screen matrix
screen_cs, target_to_screen_matrix, calibrated_screen_cs = extract_screen_cs(
primaries_measurements=rgbw_measurements_camera_native_gamut[:3],
primaries_saturation=measured_samples[Measurements.PRIMARIES_SATURATION],
white_point_measurements=rgbw_measurements_camera_native_gamut[3],
camera_native_cs=native_camera_gamut_cs,
target_cs=target_cs,
cs_cat=target_to_screen_cat,
reference_to_target_matrix=reference_to_target_matrix,
native_camera_gamut_cs=native_camera_gamut_cs, input_plate_cs=input_plate_cs,
camera_conversion_cat=camera_conversion_cat,
avoid_clipping=avoid_clipping,
macbeth_measurements_camera_native_gamut=macbeth_measurements_camera_native_gamut,
reference_samples=reference_samples
)
lut_r, lut_g, lut_b = np.array([]), np.array([]), np.array([])
if enable_EOTF_correction:
# 3: Compute LUTs for EOTF correction
eotf_ramp_target = colour.RGB_to_RGB(
eotf_ramp_camera_native_gamut, native_camera_gamut_cs, target_cs, None
)
rgbw_measurements_target = colour.RGB_to_RGB(
rgbw_measurements_camera_native_gamut, native_camera_gamut_cs, target_cs, None
)
eotf_ramp_screen_target = [ca.vector_dot(target_to_screen_matrix, m) for m in eotf_ramp_target]
rgbw_measurements_target = ca.vector_dot(target_to_screen_matrix, rgbw_measurements_target)
white_balance_offset_matrix = utils.create_white_balance_matrix(rgbw_measurements_target[3])
eotf_ramp_screen_target = [ca.vector_dot(white_balance_offset_matrix, m) for m in eotf_ramp_screen_target]
lut_r, lut_g, lut_b = eotf_correction_calculation(
eotf_ramp_screen_target,
eotf_signal_value_rgb,
delta_e_eotf_ramp,
avoid_clipping=avoid_clipping,
peak_lum=peak_lum
)
eotf_ramp_target_calibrated = colour.RGB_to_RGB(
eotf_ramp_camera_native_gamut_calibrated, native_camera_gamut_cs, target_cs, None
)
eotf_ramp_target_calibrated = ca.vector_dot(
target_to_screen_matrix, eotf_ramp_target_calibrated
)
eotf_ramp_camera_native_gamut_calibrated = colour.RGB_to_RGB(
eotf_ramp_target_calibrated, target_cs, native_camera_gamut_cs, None
)
macbeth_measurements_target_calibrated = colour.RGB_to_RGB(
macbeth_measurements_camera_native_gamut_calibrated, native_camera_gamut_cs, target_cs, None
)
macbeth_measurements_target_calibrated = ca.vector_dot(
target_to_screen_matrix, macbeth_measurements_target_calibrated
)
macbeth_measurements_camera_native_gamut_calibrated = colour.RGB_to_RGB(
macbeth_measurements_target_calibrated, target_cs, native_camera_gamut_cs, None
)
rgbw_measurements_target = apply_luts(
rgbw_measurements_target,
lut_r,
lut_g,
lut_b,
inverse=False
)
rgbw_measurements_camera_native_gamut = colour.RGB_to_RGB(
rgbw_measurements_target, target_cs, native_camera_gamut_cs, None
)
screen_cs, _, calibrated_screen_cs = extract_screen_cs(
primaries_measurements=rgbw_measurements_camera_native_gamut[:3],
primaries_saturation=measured_samples[Measurements.PRIMARIES_SATURATION],
white_point_measurements=rgbw_measurements_camera_native_gamut[3],
camera_native_cs=native_camera_gamut_cs,
target_cs=target_cs,
cs_cat=target_to_screen_cat,
reference_to_target_matrix=reference_to_target_matrix,
native_camera_gamut_cs=native_camera_gamut_cs, input_plate_cs=input_plate_cs,
camera_conversion_cat=camera_conversion_cat,
avoid_clipping=avoid_clipping,
macbeth_measurements_camera_native_gamut=macbeth_measurements_camera_native_gamut,
reference_samples=reference_samples
)
elif calculation_order == CalculationOrder.CO_EOTF_CS: # Calc 1Ds->3x3
eotf_ramp_target = colour.RGB_to_RGB(
eotf_ramp_camera_native_gamut, native_camera_gamut_cs, target_cs, None
)
rgbw_measurements_target = colour.RGB_to_RGB(
rgbw_measurements_camera_native_gamut, native_camera_gamut_cs, target_cs, None
)
eotf_white_balance_matrix = utils.create_white_balance_matrix(rgbw_measurements_target[3])
eotf_ramp_target = [ca.vector_dot(eotf_white_balance_matrix, m) for m in eotf_ramp_target]
# 1: Compute LUTs for EOTF correction
lut_r, lut_g, lut_b, = eotf_correction_calculation(
eotf_ramp_target,
eotf_signal_value_rgb,
delta_e_eotf_ramp,
avoid_clipping=avoid_clipping,
peak_lum=peak_lum
)
rgbw_measurements_target = apply_luts(
rgbw_measurements_target,
lut_r,
lut_g,
lut_b,
inverse=False
)
rgbw_measurements_camera_native_gamut = colour.RGB_to_RGB(
rgbw_measurements_target, target_cs, native_camera_gamut_cs, None
)
# 3: Create target to screen matrix
screen_cs, target_to_screen_matrix, calibrated_screen_cs = extract_screen_cs(
primaries_measurements=rgbw_measurements_camera_native_gamut[:3],
primaries_saturation=measured_samples[Measurements.PRIMARIES_SATURATION],
white_point_measurements=rgbw_measurements_camera_native_gamut[3],
camera_native_cs=native_camera_gamut_cs,
target_cs=target_cs,
cs_cat=target_to_screen_cat,
reference_to_target_matrix=reference_to_target_matrix,
native_camera_gamut_cs=native_camera_gamut_cs, input_plate_cs=input_plate_cs,
camera_conversion_cat=camera_conversion_cat,
avoid_clipping=avoid_clipping,
macbeth_measurements_camera_native_gamut=macbeth_measurements_camera_native_gamut,
reference_samples=reference_samples
)
else:
raise RuntimeError("Unknown calculation order: " + calculation_order)
# 4: Compute max distances for gamut compression
max_distances = colourspace_max_distances(
source_cs=target_cs,
destination_cs=screen_cs,
cat=target_to_screen_cat,
shadow_rolloff=gamut_compression_shadow_rolloff,
)
# Just For Plotting Needs
if enable_EOTF_correction:
eotf_ramp_camera_target_calibrated = colour.RGB_to_RGB(
eotf_ramp_camera_native_gamut_calibrated, native_camera_gamut_cs, target_cs, None
)
if calculation_order == CalculationOrder.CO_EOTF_CS:
eotf_ramp_camera_target_calibrated = [
ca.vector_dot(target_to_screen_matrix, m) for m in eotf_ramp_camera_target_calibrated]
eotf_ramp_camera_target_calibrated = apply_luts(
eotf_ramp_camera_target_calibrated,
lut_r,
lut_g,
lut_b,
inverse=False
)
eotf_ramp_camera_native_gamut_calibrated = colour.RGB_to_RGB(
eotf_ramp_camera_target_calibrated, target_cs, native_camera_gamut_cs, None
)
macbeth_measurements_target_calibrated = colour.RGB_to_RGB(
macbeth_measurements_camera_native_gamut_calibrated, native_camera_gamut_cs, target_cs, None
)
macbeth_measurements_target_calibrated = apply_luts(
macbeth_measurements_target_calibrated,
lut_r, lut_g, lut_b, inverse=False
)
macbeth_measurements_camera_native_gamut_calibrated = colour.RGB_to_RGB(
macbeth_measurements_target_calibrated, target_cs, native_camera_gamut_cs, None
)
measured_peak_lum_nits = [item * 100 for item in eotf_ramp_camera_native_gamut[-1]]
macbeth_measurements_camera_native_gamut_XYZ = np.apply_along_axis(
lambda rgb: native_camera_gamut_cs.matrix_RGB_to_XYZ.dot(rgb), 1, macbeth_measurements_camera_native_gamut
)
macbeth_measurements_camera_native_gamut_xy = colour.XYZ_to_xy(macbeth_measurements_camera_native_gamut_XYZ)
macbeth_measurements_camera_native_gamut_calibrated_XYZ = np.apply_along_axis(
lambda rgb: native_camera_gamut_cs.matrix_RGB_to_XYZ.dot(rgb), 1,
macbeth_measurements_camera_native_gamut_calibrated
)
macbeth_measurements_camera_native_gamut_calibrated_xy = colour.XYZ_to_xy(
macbeth_measurements_camera_native_gamut_calibrated_XYZ
)
# Return the results using simple, serializable types
return {
Results.PRE_CALIBRATION_SCREEN_PRIMARIES: screen_cs.primaries.tolist(),
Results.PRE_CALIBRATION_SCREEN_WHITEPOINT: screen_cs.whitepoint.tolist(),
Results.TARGET_GAMUT: target_cs.name,
Results.ENABLE_PLATE_WHITE_BALANCE: enable_plate_white_balance,
Results.ENABLE_GAMUT_COMPRESSION: enable_gamut_compression,
Results.ENABLE_EOTF_CORRECTION: enable_EOTF_correction,
Results.CALCULATION_ORDER: calculation_order,
Results.WHITE_BALANCE_MATRIX: white_balance_matrix.tolist(),
Results.TARGET_TO_SCREEN_MATRIX: target_to_screen_matrix.tolist(),
Results.REFERENCE_TO_SCREEN_MATRIX: reference_to_target_matrix.tolist(),
Results.REFERENCE_TO_TARGET_MATRIX: reference_to_target_matrix.tolist(),
Results.EOTF_LUT_R: lut_r.tolist(),
Results.EOTF_LUT_G: lut_g.tolist(),
Results.EOTF_LUT_B: lut_b.tolist(),
Results.MAX_DISTANCES: max_distances.tolist(),
Results.TARGET_EOTF: target_EOTF,
Results.NATIVE_CAMERA_GAMUT: native_camera_gamut if isinstance(native_camera_gamut, str)
else native_camera_gamut.name,
Results.OCIO_REFERENCE_GAMUT: ocio_reference_cs.name,
Results.POST_CALIBRATION_SCREEN_PRIMARIES: calibrated_screen_cs.primaries.tolist(),
Results.POST_CALIBRATION_SCREEN_WHITEPOINT: calibrated_screen_cs.whitepoint.tolist(),
Results.PRE_EOTF_RAMPS: np.array(eotf_ramp_camera_native_gamut).tolist(),
Results.POST_EOTF_RAMPS: np.array(eotf_ramp_camera_native_gamut_calibrated).tolist(),
Results.PRE_MACBETH_SAMPLES_XY: np.array(macbeth_measurements_camera_native_gamut_xy).tolist(),
Results.POST_MACBETH_SAMPLES_XY: np.array(macbeth_measurements_camera_native_gamut_calibrated_xy).tolist(),
Results.DELTA_E_RGBW: delta_e_wrgb.tolist(),
Results.DELTA_E_EOTF_RAMP: delta_e_eotf_ramp.tolist(),
Results.DELTA_E_MACBETH: delta_e_macbeth.tolist(),
Results.EXPOSURE_SCALING_FACTOR: exposure_scaling_factor,
Results.TARGET_MAX_LUM_NITS: target_max_lum_nits,
Results.MEASURED_18_PERCENT_SAMPLE: grey_measurements_white_balanced_native_gamut_green,
Results.MEASURED_MAX_LUM_NITS: measured_peak_lum_nits,
Results.REFERENCE_EOTF_RAMP: eotf_signal_values,
Results.TARGET_TO_XYZ_MATRIX: target_to_XYZ_matrix.tolist(),
Results.REFERENCE_TO_XYZ_MATRIX: reference_to_XYZ_matrix.tolist(),
Results.REFERENCE_TO_INPUT_MATRIX: reference_to_input_matrix.tolist(),
Results.MAX_WHITE_DELTA: max_white_delta,
Results.EOTF_LINEARITY: eotf_linearity,
Results.AVOID_CLIPPING: avoid_clipping
}