|
import numpy as np |
|
|
|
|
|
def rgb2gray(data): |
|
return 0.299 * data[:, :, 0] + \ |
|
0.587 * data[:, :, 1] + \ |
|
0.114 * data[:, :, 2] |
|
|
|
|
|
def rgb2ycc(data, rule="bt601"): |
|
|
|
kr_kb_dict = {"bt601": [0.299, 0.114], |
|
"bt709": [0.2126, 0.0722], |
|
"bt2020": [0.2627, 0.0593]} |
|
|
|
kr = kr_kb_dict[rule][0] |
|
kb = kr_kb_dict[rule][1] |
|
kg = 1 - (kr + kb) |
|
|
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
output[:, :, 0] = kr * data[:, :, 0] + \ |
|
kg * data[:, :, 1] + \ |
|
kb * data[:, :, 2] |
|
output[:, :, 1] = 0.5 * ((data[:, :, 2] - output[:, :, 0]) / (1 - kb)) |
|
output[:, :, 2] = 0.5 * ((data[:, :, 0] - output[:, :, 0]) / (1 - kr)) |
|
|
|
return output |
|
|
|
|
|
def ycc2rgb(data, rule="bt601"): |
|
|
|
kr_kb_dict = {"bt601": [0.299, 0.114], |
|
"bt709": [0.2126, 0.0722], |
|
"bt2020": [0.2627, 0.0593]} |
|
|
|
kr = kr_kb_dict[rule][0] |
|
kb = kr_kb_dict[rule][1] |
|
kg = 1 - (kr + kb) |
|
|
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
output[:, :, 0] = 2. * data[:, :, 2] * (1 - kr) + data[:, :, 0] |
|
output[:, :, 2] = 2. * data[:, :, 1] * (1 - kb) + data[:, :, 0] |
|
output[:, :, 1] = (data[:, :, 0] - kr * output[:, :, 0] - kb * output[:, :, 2]) / kg |
|
|
|
return output |
|
|
|
|
|
def degamma_srgb(data, clip_range=[0, 65535]): |
|
|
|
data = np.clip(data, clip_range[0], clip_range[1]) |
|
data = np.divide(data, clip_range[1]) |
|
|
|
data = np.asarray(data) |
|
mask = data > 0.04045 |
|
|
|
|
|
|
|
data[mask] += 0.055 |
|
data[mask] /= 1.055 |
|
data[mask] **= 2.4 |
|
|
|
data[np.invert(mask)] /= 12.92 |
|
|
|
|
|
return np.clip(data * clip_range[1], clip_range[0], clip_range[1]) |
|
|
|
|
|
def degamma_adobe_rgb_1998(data, clip_range=[0, 65535]): |
|
|
|
data = np.clip(data, clip_range[0], clip_range[1]) |
|
data = np.divide(data, clip_range[1]) |
|
|
|
data = np.power(data, 2.2) |
|
|
|
|
|
return np.clip(data * clip_range[1], clip_range[0], clip_range[1]) |
|
|
|
|
|
def rgb2xyz(data, color_space="srgb", clip_range=[0, 255]): |
|
|
|
|
|
if color_space == "srgb": |
|
|
|
data = degamma_srgb(data, clip_range) |
|
data = np.float32(data) |
|
data = np.divide(data, clip_range[1]) |
|
|
|
|
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
output[:, :, 0] = data[:, :, 0] * 0.4124 + data[:, :, 1] * 0.3576 + data[:, :, 2] * 0.1805 |
|
output[:, :, 1] = data[:, :, 0] * 0.2126 + data[:, :, 1] * 0.7152 + data[:, :, 2] * 0.0722 |
|
output[:, :, 2] = data[:, :, 0] * 0.0193 + data[:, :, 1] * 0.1192 + data[:, :, 2] * 0.9505 |
|
elif color_space == "adobe-rgb-1998": |
|
|
|
data = degamma_adobe_rgb_1998(data, clip_range) |
|
data = np.float32(data) |
|
data = np.divide(data, clip_range[1]) |
|
|
|
|
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
output[:, :, 0] = data[:, :, 0] * 0.5767309 + data[:, :, 1] * 0.1855540 + data[:, :, 2] * 0.1881852 |
|
output[:, :, 1] = data[:, :, 0] * 0.2973769 + data[:, :, 1] * 0.6273491 + data[:, :, 2] * 0.0752741 |
|
output[:, :, 2] = data[:, :, 0] * 0.0270343 + data[:, :, 1] * 0.0706872 + data[:, :, 2] * 0.9911085 |
|
elif color_space == "linear": |
|
|
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
data = np.float32(data) |
|
data = np.divide(data, clip_range[1]) |
|
output[:, :, 0] = data[:, :, 0] * 0.4124 + data[:, :, 1] * 0.3576 + data[:, :, 2] * 0.1805 |
|
output[:, :, 1] = data[:, :, 0] * 0.2126 + data[:, :, 1] * 0.7152 + data[:, :, 2] * 0.0722 |
|
output[:, :, 2] = data[:, :, 0] * 0.0193 + data[:, :, 1] * 0.1192 + data[:, :, 2] * 0.9505 |
|
else: |
|
print("Warning! color_space must be srgb or adobe-rgb-1998.") |
|
return |
|
|
|
return output |
|
|
|
|
|
def gamma_srgb(data, clip_range=[0, 65535]): |
|
|
|
data = np.clip(data, clip_range[0], clip_range[1]) |
|
data = np.divide(data, clip_range[1]) |
|
|
|
data = np.asarray(data) |
|
mask = data > 0.0031308 |
|
|
|
|
|
|
|
data[mask] **= 0.4167 |
|
data[mask] *= 1.055 |
|
data[mask] -= 0.055 |
|
|
|
data[np.invert(mask)] *= 12.92 |
|
|
|
|
|
return np.clip(data * clip_range[1], clip_range[0], clip_range[1]) |
|
|
|
|
|
def gamma_adobe_rgb_1998(data, clip_range=[0, 65535]): |
|
|
|
data = np.clip(data, clip_range[0], clip_range[1]) |
|
data = np.divide(data, clip_range[1]) |
|
|
|
data = np.power(data, 0.4545) |
|
|
|
|
|
return np.clip(data * clip_range[1], clip_range[0], clip_range[1]) |
|
|
|
|
|
def xyz2rgb(data, color_space="srgb", clip_range=[0, 255]): |
|
|
|
|
|
|
|
|
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
|
|
if color_space == "srgb": |
|
|
|
output[:, :, 0] = data[:, :, 0] * 3.2406 + data[:, :, 1] * -1.5372 + data[:, :, 2] * -0.4986 |
|
output[:, :, 1] = data[:, :, 0] * -0.9689 + data[:, :, 1] * 1.8758 + data[:, :, 2] * 0.0415 |
|
output[:, :, 2] = data[:, :, 0] * 0.0557 + data[:, :, 1] * -0.2040 + data[:, :, 2] * 1.0570 |
|
|
|
|
|
output = gamma_srgb(output * clip_range[1], clip_range) |
|
elif color_space == "adobe-rgb-1998": |
|
|
|
output[:, :, 0] = data[:, :, 0] * 2.0413690 + data[:, :, 1] * -0.5649464 + data[:, :, 2] * -0.3446944 |
|
output[:, :, 1] = data[:, :, 0] * -0.9692660 + data[:, :, 1] * 1.8760108 + data[:, :, 2] * 0.0415560 |
|
output[:, :, 2] = data[:, :, 0] * 0.0134474 + data[:, :, 1] * -0.1183897 + data[:, :, 2] * 1.0154096 |
|
|
|
|
|
output = gamma_adobe_rgb_1998(output * clip_range[1], clip_range) |
|
elif color_space == "linear": |
|
|
|
|
|
output[:, :, 0] = data[:, :, 0] * 3.2406 + data[:, :, 1] * -1.5372 + data[:, :, 2] * -0.4986 |
|
output[:, :, 1] = data[:, :, 0] * -0.9689 + data[:, :, 1] * 1.8758 + data[:, :, 2] * 0.0415 |
|
output[:, :, 2] = data[:, :, 0] * 0.0557 + data[:, :, 1] * -0.2040 + data[:, :, 2] * 1.0570 |
|
|
|
|
|
output = output * clip_range[1] |
|
else: |
|
print("Warning! color_space must be srgb or adobe-rgb-1998.") |
|
return |
|
|
|
return output |
|
|
|
|
|
def get_xyz_reference(cie_version="1931", illuminant="d65"): |
|
if cie_version == "1931": |
|
xyz_reference_dictionary = {"A": [109.850, 100.0, 35.585], |
|
"B": [99.0927, 100.0, 85.313], |
|
"C": [98.074, 100.0, 118.232], |
|
"d50": [96.422, 100.0, 82.521], |
|
"d55": [95.682, 100.0, 92.149], |
|
"d65": [95.047, 100.0, 108.883], |
|
"d75": [94.972, 100.0, 122.638], |
|
"E": [100.0, 100.0, 100.0], |
|
"F1": [92.834, 100.0, 103.665], |
|
"F2": [99.187, 100.0, 67.395], |
|
"F3": [103.754, 100.0, 49.861], |
|
"F4": [109.147, 100.0, 38.813], |
|
"F5": [90.872, 100.0, 98.723], |
|
"F6": [97.309, 100.0, 60.191], |
|
"F7": [95.044, 100.0, 108.755], |
|
"F8": [96.413, 100.0, 82.333], |
|
"F9": [100.365, 100.0, 67.868], |
|
"F10": [96.174, 100.0, 81.712], |
|
"F11": [100.966, 100.0, 64.370], |
|
"F12": [108.046, 100.0, 39.228]} |
|
elif cie_version == "1964": |
|
xyz_reference_dictionary = {"A": [111.144, 100.0, 35.200], |
|
"B": [99.178, 100.0, 84.3493], |
|
"C": [97.285, 100.0, 116.145], |
|
"D50": [96.720, 100.0, 81.427], |
|
"D55": [95.799, 100.0, 90.926], |
|
"D65": [94.811, 100.0, 107.304], |
|
"D75": [94.416, 100.0, 120.641], |
|
"E": [100.0, 100.0, 100.0], |
|
"F1": [94.791, 100.0, 103.191], |
|
"F2": [103.280, 100.0, 69.026], |
|
"F3": [108.968, 100.0, 51.965], |
|
"F4": [114.961, 100.0, 40.963], |
|
"F5": [93.369, 100.0, 98.636], |
|
"F6": [102.148, 100.0, 62.074], |
|
"F7": [95.792, 100.0, 107.687], |
|
"F8": [97.115, 100.0, 81.135], |
|
"F9": [102.116, 100.0, 67.826], |
|
"F10": [99.001, 100.0, 83.134], |
|
"F11": [103.866, 100.0, 65.627], |
|
"F12": [111.428, 100.0, 40.353]} |
|
else: |
|
print("Warning! cie_version must be 1931 or 1964.") |
|
return |
|
return np.divide(xyz_reference_dictionary[illuminant], 100.0) |
|
|
|
|
|
def xyz2lab(data, cie_version="1931", illuminant="d65"): |
|
xyz_reference = get_xyz_reference(cie_version, illuminant) |
|
|
|
data = data |
|
data[:, :, 0] = data[:, :, 0] / xyz_reference[0] |
|
data[:, :, 1] = data[:, :, 1] / xyz_reference[1] |
|
data[:, :, 2] = data[:, :, 2] / xyz_reference[2] |
|
|
|
data = np.asarray(data) |
|
|
|
|
|
|
|
mask = data > 0.008856 |
|
data[mask] **= 1. / 3. |
|
data[np.invert(mask)] *= 7.787 |
|
data[np.invert(mask)] += 16. / 116. |
|
|
|
data = np.float32(data) |
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
output[:, :, 0] = 116. * data[:, :, 1] - 16. |
|
output[:, :, 1] = 500. * (data[:, :, 0] - data[:, :, 1]) |
|
output[:, :, 2] = 200. * (data[:, :, 1] - data[:, :, 2]) |
|
|
|
return output |
|
|
|
|
|
def lab2xyz(data, cie_version="1931", illuminant="d65"): |
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
|
|
output[:, :, 1] = (data[:, :, 0] + 16.) / 116. |
|
output[:, :, 0] = (data[:, :, 1] / 500.) + output[:, :, 1] |
|
output[:, :, 2] = output[:, :, 1] - (data[:, :, 2] / 200.) |
|
|
|
|
|
|
|
output = np.asarray(output) |
|
mask = output > 0.008856 |
|
output[mask] **= 3. |
|
output[np.invert(mask)] -= 16 / 116 |
|
output[np.invert(mask)] /= 7.787 |
|
|
|
xyz_reference = get_xyz_reference(cie_version, illuminant) |
|
|
|
output = np.float32(output) |
|
output[:, :, 0] = output[:, :, 0] * xyz_reference[0] |
|
output[:, :, 1] = output[:, :, 1] * xyz_reference[1] |
|
output[:, :, 2] = output[:, :, 2] * xyz_reference[2] |
|
|
|
return output |
|
|
|
|
|
def lab2lch(data): |
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
|
|
output[:, :, 0] = data[:, :, 0] |
|
output[:, :, 1] = np.power(np.power(data[:, :, 1], 2) + np.power(data[:, :, 2], 2), 0.5) |
|
output[:, :, 2] = np.arctan2(data[:, :, 2], data[:, :, 1]) * 180 / np.pi |
|
|
|
return output |
|
|
|
|
|
def lch2lab(data): |
|
output = np.empty(np.shape(data), dtype=np.float32) |
|
|
|
output[:, :, 0] = data[:, :, 0] |
|
output[:, :, 1] = np.multiply(np.cos(data[:, :, 2] * np.pi / 180), data[:, :, 1]) |
|
output[:, :, 2] = np.multiply(np.sin(data[:, :, 2] * np.pi / 180), data[:, :, 1]) |
|
|
|
return output |
|
|