Created
September 5, 2024 06:12
-
-
Save qvoid/50904a6ebfa6db42bf6f595a39a4b558 to your computer and use it in GitHub Desktop.
Coeffecients calculation about translation between RGB and YCbCr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
# BT.601 coefficients | |
K_r = 0.2990 | |
K_b = 0.1140 | |
# 0.5870 | |
K_g = 1 - K_r - K_b | |
# # BT.709 coefficients | |
# K_r = 0.2126 | |
# K_b = 0.0722 | |
# # 0.7152 | |
# K_g = 1 - K_r - K_b | |
# | |
# # BT.2020 coefficients | |
# K_r = 0.2627 | |
# K_b = 0.0593 | |
# # 0.6780 | |
# K_g = 1 - K_r - K_b | |
K_y = np.array([K_r, K_g, K_b]) | |
K_u = np.array([0, 0, 1]) - K_y | |
K_v = np.array([1, 0, 0]) - K_y | |
analog_coeff = np.array([ | |
[K_r, K_g, K_b], | |
[-K_r, -K_g, 1 - K_b], | |
[1- K_r, -K_g, -K_b] | |
]) | |
print("step 1. analog_coeff:\n", analog_coeff) | |
# re-normalize to [-0.5, 0.5] | |
K_cb = K_u / (2 * (1 - K_b)) | |
K_cr = K_v / (2 * (1 - K_r)) | |
re_normalized_analog_coeff = analog_coeff * np.array([[1], [1 / 2 / (1 - K_b)], [1 / 2 / (1 - K_r)]]) | |
print("step 2. re-normalized analog_coeff:\n", re_normalized_analog_coeff) | |
# sample precision, could be 8, 10 or 12 | |
n = 8 | |
# 1, 4, 16 | |
D = pow(2, n - 8) | |
YCbCr_value = D *np.array([[16], [16], [16]]) | |
RGB_value = D * np.array([[235], [235], [235]]) | |
# limit RGB to limit YCbCr | |
K_Y = K_y | |
K_Cb = 224 / 219 * K_cb | |
K_Cr = 224 / 219 * K_cr | |
offset = D * np.array([[0], [128], [128]]) | |
coeff = np.array([K_Y, K_Cb, K_Cr]) | |
print("limit range RGB to limit range YCbCr coefficients: \n", coeff) | |
YCbCr_value = np.around(np.dot(coeff, RGB_value)) + offset | |
print("From RGB \n", RGB_value, "\n to YCbCr \n", YCbCr_value) | |
# for i in range (8, 17): | |
# print("----precision: ", i) | |
# print("K_Y: ", np.around(K_Y * pow(2, i))) | |
# print("K_Cb: ", np.around(K_Cb * pow(2, i))) | |
# print("K_Cr: ", np.around(K_Cr * pow(2, i))) | |
# full range RGB to limit YCbCr | |
K_Y = 219 / 255 * K_y | |
K_Cb = 224 / 255 * K_cb | |
K_Cr = 224 / 255 * K_cr | |
offset = D * np.array([[16], [128], [128]]) | |
coeff = np.array([K_Y, K_Cb, K_Cr]) | |
print("full range RGB to limit range YCbCr coefficients: \n", coeff) | |
YCbCr_value = np.around(np.dot(coeff, RGB_value)) + offset | |
print("From RGB \n", RGB_value, "\n to YCbCr \n", YCbCr_value) | |
# limit RGB to full YCbCr | |
K_Y = 255 / 219 * K_y | |
K_Cb = 255 / 219 * K_cb | |
K_Cr = 255 / 219 * K_cr | |
offset = D * np.array([[-255 * 16 / 219], [128], [128]]) | |
coeff = np.array([K_Y, K_Cb, K_Cr]) | |
print("limit range RGB to full range YCbCr coefficients: \n", coeff) | |
YCbCr_value = np.around(np.dot(coeff, RGB_value)) + offset | |
print("From RGB \n", RGB_value, "\n to YCbCr \n", YCbCr_value) | |
# full RGB to full YCbCr | |
K_Y = K_y | |
K_Cb = K_cb | |
K_Cr = K_cr | |
offset = D * np.array([[0], [128], [128]]) | |
coeff = np.array([K_Y, K_Cb, K_Cr]) | |
print("full range RGB to full range YCbCr coefficients: \n", coeff) | |
YCbCr_value = np.around(np.dot(coeff, RGB_value)) + offset | |
print("From RGB \n", RGB_value, "\n to YCbCr \n", YCbCr_value) | |
################################ | |
# YUV to RGB | |
################################ | |
# construct analog RGB to analog YUV | |
K_R_prime = np.array([1, 0, 2 * (1 - K_r)]) | |
K_B_prime = np.array([1, 2 * (1 - K_b), 0]) | |
K_G_prime = np.array([1, -2 * K_b * (1 - K_b) / K_g, -2 * K_r * (1 - K_r) / K_g]) | |
print("From YUV to RGB analog coefficients:\n") | |
print(K_R_prime) | |
print(K_G_prime) | |
print(K_B_prime) | |
# From limit YCbCr to limit RGB | |
K_R = np.array([1, 0, 2 * (1 - K_r) * 219 / 224]) | |
K_B = np.array([1, 2 * (1 - K_b) * 219 / 224, 0]) | |
K_G = np.array([1, -2 * K_b * (1 - K_b) / K_g * 219 / 224, -2 * K_r * (1 - K_r) / K_g * 219 / 224]) | |
ycbcr_offset = D * np.array([[0], [-128], [-128]]) | |
offset = D * np.array([[0], [0], [0]]) | |
yuv2rgb_coeff = np.array([K_R, K_G, K_B]) | |
print("limit range YCbCr to limit range RGB coefficients: \n", yuv2rgb_coeff) | |
RGB_value = np.around(np.dot(yuv2rgb_coeff, YCbCr_value + ycbcr_offset) + offset) | |
print("From YCbCr \n", YCbCr_value, "\n to RGB \n", RGB_value) | |
# From full YCbCr to limit RGB | |
K_R = 219 / 255 * K_R_prime | |
K_B = 219 / 255 * K_B_prime | |
K_G = 219 / 255 * K_G_prime | |
ycbcr_offset = D * np.array([[0], [-128], [-128]]) | |
offset = D * np.array([[16], [16], [16]]) | |
yuv2rgb_coeff = np.array([K_R, K_G, K_B]) | |
print("full range YCbCr to limit range RGB coefficients: \n", yuv2rgb_coeff) | |
RGB_value = np.around(np.dot(yuv2rgb_coeff, YCbCr_value + ycbcr_offset) + offset) | |
print("From YCbCr \n", YCbCr_value, "\n to RGB \n", RGB_value) | |
# From limit YCbCr to full RGB | |
K_R = np.array([255/219, 0, 2 * (1 - K_r) * 255 / 224]) | |
K_B = np.array([255/219, 2 * (1 - K_b) * 255 / 224, 0]) | |
K_G = np.array([255/219, -2 * K_b * (1 - K_b) / K_g * 255 / 224, -2 * K_r * (1 - K_r) / K_g * 255 / 224]) | |
ycbcr_offset = D * np.array([[-16], [-128], [-128]]) | |
offset = D * np.array([[0], [0], [0]]) | |
yuv2rgb_coeff = np.array([K_R, K_G, K_B]) | |
print("limit range YCbCr to full range RGB coefficients: \n", yuv2rgb_coeff) | |
RGB_value = np.around(np.dot(yuv2rgb_coeff, YCbCr_value + ycbcr_offset) + offset) | |
print("From YCbCr \n", YCbCr_value, "\n to RGB \n", RGB_value) | |
# From full YCbCr to full RGB | |
K_R = K_R_prime | |
K_B = K_B_prime | |
K_G = K_G_prime | |
ycbcr_offset = D * np.array([[0], [-128], [-128]]) | |
offset = D * np.array([[0], [0], [0]]) | |
yuv2rgb_coeff = np.array([K_R, K_G, K_B]) | |
print("full range YCbCr to full range RGB coefficients: \n", yuv2rgb_coeff) | |
RGB_value = np.around(np.dot(yuv2rgb_coeff, YCbCr_value + ycbcr_offset) + offset) | |
print("From YCbCr \n", YCbCr_value, "\n to RGB \n", RGB_value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment