Skip to content

Instantly share code, notes, and snippets.

@qvoid
Created September 5, 2024 06:12
Show Gist options
  • Save qvoid/50904a6ebfa6db42bf6f595a39a4b558 to your computer and use it in GitHub Desktop.
Save qvoid/50904a6ebfa6db42bf6f595a39a4b558 to your computer and use it in GitHub Desktop.
Coeffecients calculation about translation between RGB and YCbCr
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