Spaces:
Sleeping
Sleeping
File size: 3,878 Bytes
346b70f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
import numpy as np
RGB_MAX_VALUE = 255.0
SRGB_GAMMA = 2.4
SRGB_LINEAR_THRESHOLD = 0.04045
SRGB_OFFSET = 0.055
SRGB_SCALE = 1.055
SRGB_LINEAR_SCALE = 12.92
D65_ILLUMINANT_X = 0.95047
D65_ILLUMINANT_Z = 1.08883
LAB_EPSILON = 216 / 24389
LAB_KAPPA = 24389 / 27
LAB_DELTA = 16 / 116
def rgb_to_lab(rgb):
normalized_rgb = rgb.astype(np.float32) / RGB_MAX_VALUE
linear_rgb = apply_inverse_srgb_gamma(normalized_rgb)
xyz_values = convert_linear_rgb_to_xyz(linear_rgb)
normalized_xyz = normalize_xyz_by_d65_illuminant(xyz_values)
return convert_xyz_to_lab(normalized_xyz)
def apply_inverse_srgb_gamma(normalized_rgb):
above_threshold = normalized_rgb > SRGB_LINEAR_THRESHOLD
linearized_high_values = ((normalized_rgb + SRGB_OFFSET) / SRGB_SCALE) ** SRGB_GAMMA
linearized_low_values = normalized_rgb / SRGB_LINEAR_SCALE
return np.where(above_threshold, linearized_high_values, linearized_low_values)
def convert_linear_rgb_to_xyz(linear_rgb):
srgb_to_xyz_matrix = np.array(
[
[0.4124564, 0.3575761, 0.1804375],
[0.2126729, 0.7151522, 0.0721750],
[0.0193339, 0.1191920, 0.9503041],
],
dtype=np.float32,
)
return linear_rgb @ srgb_to_xyz_matrix.T
def normalize_xyz_by_d65_illuminant(xyz):
normalized = xyz.copy()
normalized[:, 0] /= D65_ILLUMINANT_X
normalized[:, 2] /= D65_ILLUMINANT_Z
return normalized
def convert_xyz_to_lab(normalized_xyz):
above_epsilon = normalized_xyz > LAB_EPSILON
cubic_root_values = normalized_xyz ** (1 / 3)
linear_scaled_values = (LAB_KAPPA * normalized_xyz + 16) / 116
f_transformed = np.where(above_epsilon, cubic_root_values, linear_scaled_values)
lab_values = np.zeros_like(normalized_xyz)
lab_values[:, 0] = 116 * f_transformed[:, 1] - 16
lab_values[:, 1] = 500 * (f_transformed[:, 0] - f_transformed[:, 1])
lab_values[:, 2] = 200 * (f_transformed[:, 1] - f_transformed[:, 2])
return lab_values
def lab_to_rgb(lab):
xyz_values = convert_lab_to_xyz(lab)
linear_rgb = convert_xyz_to_linear_rgb(xyz_values)
normalized_rgb = apply_srgb_gamma(linear_rgb)
return convert_normalized_to_8bit_rgb(normalized_rgb)
def convert_lab_to_xyz(lab):
f_y = (lab[:, 0] + 16) / 116
f_x = lab[:, 1] / 500 + f_y
f_z = f_y - lab[:, 2] / 200
x_above_epsilon = f_x**3 > LAB_EPSILON
y_above_epsilon = lab[:, 0] > LAB_KAPPA * LAB_EPSILON
z_above_epsilon = f_z**3 > LAB_EPSILON
xyz_values = np.zeros((len(lab), 3), dtype=np.float32)
x_cubic = f_x**3
x_linear = (116 * f_x - 16) / LAB_KAPPA
xyz_values[:, 0] = np.where(x_above_epsilon, x_cubic, x_linear) * D65_ILLUMINANT_X
y_cubic = f_y**3
y_linear = lab[:, 0] / LAB_KAPPA
xyz_values[:, 1] = np.where(y_above_epsilon, y_cubic, y_linear)
z_cubic = f_z**3
z_linear = (116 * f_z - 16) / LAB_KAPPA
xyz_values[:, 2] = np.where(z_above_epsilon, z_cubic, z_linear) * D65_ILLUMINANT_Z
return xyz_values
def convert_xyz_to_linear_rgb(xyz):
xyz_to_srgb_matrix = np.array(
[
[3.2404542, -1.5371385, -0.4985314],
[-0.9692660, 1.8760108, 0.0415560],
[0.0556434, -0.2040259, 1.0572252],
],
dtype=np.float32,
)
return xyz @ xyz_to_srgb_matrix.T
def apply_srgb_gamma(linear_rgb):
SRGB_LINEAR_CUTOFF = 0.0031308
above_cutoff = linear_rgb > SRGB_LINEAR_CUTOFF
gamma_corrected_high = SRGB_SCALE * (linear_rgb ** (1 / SRGB_GAMMA)) - SRGB_OFFSET
gamma_corrected_low = SRGB_LINEAR_SCALE * linear_rgb
return np.where(above_cutoff, gamma_corrected_high, gamma_corrected_low)
def convert_normalized_to_8bit_rgb(normalized_rgb):
scaled_values = normalized_rgb * RGB_MAX_VALUE
clipped_values = np.clip(scaled_values, 0, RGB_MAX_VALUE)
return clipped_values.astype(np.uint8)
|