"""Precompute CAM16-UCS colour tuples for all 2^24 RGB tuples. This 192MB data file is used to convert from RGB to CAM16-UCS colour space for purposes of computing (approximate) perceptual difference between pairs of colours when optimizing the image conversion (since this perceptual difference corresponds to the Euclidean distance in this colour space) """ import os import colour import numpy as np def srgb_to_linear_rgb_array(a: np.ndarray, gamma=2.4) -> np.ndarray: return np.where(a <= 0.04045, a / 12.92, ((a + 0.055) / 1.055) ** gamma) DATA_DIR = 'data' def main(): try: os.mkdir(DATA_DIR, mode=0o755) except FileExistsError: pass print("Precomputing conversion matrix from 24-bit RGB to CAM16UCS colour " "space") # Compute matrix of all 24-bit RGB values, normalized to 0..1 range bits24 = np.arange(2 ** 24, dtype=np.uint32).reshape(-1, 1) all_rgb24 = np.concatenate( [bits24 >> 16 & 0xff, bits24 >> 8 & 0xff, bits24 & 0xff], axis=1).astype(np.float32) / 255 del bits24 with colour.utilities.suppress_warnings(colour_usage_warnings=True): # Compute matrix of corresponding CAM16UCS colour values, indexed # by 24-bit RGB value rgb24_to_cam16ucs = colour.convert(all_rgb24, "RGB", "CAM16UCS").astype( np.float32) del all_rgb24 np.save("%s/rgb24_to_cam16ucs.npy" % DATA_DIR, np.ascontiguousarray( rgb24_to_cam16ucs)) del rgb24_to_cam16ucs print("Precomputing conversion matrix from 12-bit //gs RGB to CAM16UCS " "colour space") # Compute matrix of all 12-bit RGB values, normalized to 0..1 range bits12 = np.arange(2 ** 12, dtype=np.uint32).reshape(-1, 1) r = bits12 >> 8 g = (bits12 >> 4) & 0xf b = bits12 & 0xf all_rgb12 = np.concatenate( [(r << 4) | r, (g << 4) | g, (b << 4) | b], axis=1).astype( np.float32) / 255 del bits12, r, g, b # //gs RGB values use gamma-corrected Rec.601 RGB colour space. We need to # convert to Rec.709 RGB as preparation for converting to CAM16UCS. We # do this via the YCbCr intermediate color model. rgb12_iigs = np.clip(srgb_to_linear_rgb_array( np.clip(colour.YCbCr_to_RGB( colour.RGB_to_YCbCr( all_rgb12, K=colour.WEIGHTS_YCBCR[ 'ITU-R BT.601']), K=colour.WEIGHTS_YCBCR['ITU-R BT.709']), 0, 1)), 0, 1) with colour.utilities.suppress_warnings(colour_usage_warnings=True): # Compute matrix of corresponding CAM16UCS colour values, indexed # by 12-bit //gs RGB value rgb12_iigs_to_cam16ucs = colour.convert( rgb12_iigs, "RGB", "CAM16UCS").astype(np.float32) del rgb12_iigs np.save("%s/rgb12_iigs_to_cam16ucs.npy" % DATA_DIR, np.ascontiguousarray( rgb12_iigs_to_cam16ucs)) del rgb12_iigs_to_cam16ucs if __name__ == "__main__": main()