123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- """
- author: chaohui.yang
- date: 2022-09-07
- """
- import math
- from abc import abstractmethod, ABCMeta
- from datetime import datetime
- import numpy as np
- from matplotlib.ticker import MultipleLocator
- from scipy import interpolate
- import xlrd
- import json
- import jsbeautifier
- import matplotlib.pyplot as plt
- np.set_printoptions(precision=5)
- np.set_printoptions(suppress=True)
- def readDataFromExcel(filePath: str):
- """
- 从EXCEL中读取目标x,y以及gamma值,以及测量的XYZ值
- :param filePath: ecxel路径
- :return: 转换关系矩阵RGB_2_XYZ, 包含11组测量值的矩阵XYZ_11, 包含11个测量值索引的数组XYZ11_index,target_x, target_y, target_gamma
- """
- workbook = xlrd.open_workbook(filePath)
- sheet = workbook.sheets()[0]
- # 使用表格中定义的target_x,target_y,以及target_gamma值
- target_x = sheet.cell_value(8, 1)
- target_y = sheet.cell_value(9, 1)
- target_gamma = sheet.cell_value(10, 1)
- print(f"从表格读取到:\ntarget_x = {target_x}\ntarget_y = {target_y}\ntarget_gamma = {target_gamma}\n")
- XYZ11_index = sheet.row_values(1, 1)[3:14]
- rows_X = sheet.row_values(2, 1)[0:14] # 获取第X行内容
- rows_Y = sheet.row_values(3, 1)[0:14] # 获取第Y行内容
- rows_Z = sheet.row_values(4, 1)[0:14] # 获取第Z行内容
- XYZ_all = [[rows_X[i], rows_Y[i], rows_Z[i]] for i in range(len(rows_X))]
- XYZ_11 = np.array(XYZ_all[3:14]).T
- RGB_2_XYZ = np.array(XYZ_all[0:3]).T # np.array([XYZ[0], XYZ[1], XYZ[2]])
- return RGB_2_XYZ, XYZ_11, XYZ11_index, target_x, target_y, target_gamma
- def cal_target_XYZ11(XYZ_2_RGB, target_x, target_y, target_gamma):
- """
- 根据“转换关系”矩阵XYZ_2_RGB,计算出11个目标XYZ值
- :param XYZ_2_RGB: 表示“转换关系”的矩阵,也就是测量而得的R/G/B对应的XYZ值所合成的矩阵(3X3)
- :param target_x:
- :param target_y:
- :param target_gamma:
- :return: targetXYZ11:包含11个目标XYZ值的矩阵(3X11)
- """
- print("以下求最大可行目标亮度值....")
- targetY_list = []
- for i in range(0, 3):
- targetY = 1 / (XYZ_2_RGB[i][0] * target_x / target_y + XYZ_2_RGB[i][1] + XYZ_2_RGB[i][2] * (
- 1 - target_x - target_y) / target_y)
- targetY_list.append(targetY)
- print(i + 1, "式子的解:targetY = ", targetY)
- targetY_max = round(min(targetY_list), 5)
- print("最大可行目标亮度值: targetY_max = ", targetY_max)
- targetXYZ11 = np.zeros((3, 11))
- # print("targetXYZ11: \n", targetXYZ11)
- # 算出11个target_XYZ
- for i in range(0, 11):
- targetXYZ11[1][i] = targetY_max * ((1 - 0.1 * i) ** target_gamma)
- targetXYZ11[0][i] = (targetXYZ11[1][i] * target_x) / target_y
- targetXYZ11[2][i] = targetXYZ11[1][i] * (1 - target_x - target_y) / target_y
- # print("11个目标target_XYZ值: \n", targetXYZ11)
- return targetXYZ11
- # 插值方法
- def interpolate_to_256(ori_matrix, ori_matrix_index):
- """
- 插值算法。根据输入数组和对应元素序列,插值使得最终为256个元素
- :param ori_matrix: 输入矩阵(n x m),通常每行分别对应R/G/B值或X/Y/Z值
- :param ori_matrix_index: 对应每列矩阵元素的索引值序列(1 x m)
- :return: output_matrix: 输出插值后的矩阵(nx256)
- """
- # print(f"待扩充矩阵序列间隔为ori_index :\n{ori_matrix_index}")
- # print("待扩充矩阵为: \n", ori_matrix)
- new_index = np.arange(0, 256, 1)
- # 插值方式kind = linear|slinear|quadratic|cubic
- f = interpolate.interp1d(ori_matrix_index, ori_matrix, kind="linear")
- output_matrix = f(new_index)
- # print("扩充后的矩阵为 :\n", output_matrix)
- return output_matrix
- def cal_RGB_by_XYZ(transMatrix, matrix_XYZ):
- """
- 根据逆矩阵和XYZ计算RGB
- :param transMatrix: RGB TO XYZ的转换矩阵逆矩阵(3X3)
- :param matrix_XYZ: XYZ矩阵(3X3)
- :return: RGB矩阵(3X3)
- """
- matrix_RGB = np.dot(transMatrix, matrix_XYZ)
- return matrix_RGB
- def calActualRGB11(targetRGB11, targetXYZ11, XYZ256):
- """
- 根据11个目标RGB值、目标XYZ值以及测量再插值而得的256个XYZ,算出实际需要的11个RGB值
- :param targetRGB11: 包含11个目标RGB值的3x11矩阵
- :param targetXYZ11: 包含11个目标XYZ值的3x11矩阵
- :param XYZ256: 包含256个由测量的XYZ值的3x256矩阵
- :return: actualRGB11:包含11个实际需要的RGB值的3x11矩阵
- """
- print(f"以下开始计算实际需要的11组RGB值...\n")
- actualRGB11 = np.zeros([3, 11])
- targetRGB11 = targetRGB11.T[::-1].T
- print("翻转的targetRGB11 is: ", targetRGB11)
- targetXYZ11 = targetXYZ11.T[::-1].T
- print("翻转的targetXYZ11 is: ", targetXYZ11)
- for n in range(0, 3):
- for m in range(1, 11):
- distances = []
- for index in range(0, 256):
- distance = abs(XYZ256[n, index] - targetXYZ11[n, m])
- distances.append(distance)
- # print(f"targetXYZ11[{n}][{m}]对应的XYZ256的距离为:\n{distances}")
- min_index = distances.index(min(distances))
- print(f"目标点targetXYZ11[{n}][{m}] , 对应XYZ256中最短距离的元素值index为: {min_index}")
- # 当min_index刚好为首、尾index时无需参与gamma计算,直接取对应的target值
- if min_index == 0 or min_index == 255:
- actualRGB11[n, m] = targetRGB11[n, m]
- print(f"该点不计算panel gamma值\n")
- else:
- panelGamma = round(math.log((XYZ256[n, min_index]) / XYZ256[n, 255]) / math.log((min_index) / 255), 5)
- print(f"对应targetXYZ11[{n}][{m}]最短距离的XYZ256[{n}][{min_index}]的panel gamma值为:{panelGamma}\n")
- # 当panelGamma算出为0时,无法数学计算直接取对应的target值
- if panelGamma == 0:
- actualRGB11[n, m] = targetRGB11[n, m]
- else:
- actualRGB11[n, m] = (targetRGB11[n, m]) ** (1 / panelGamma)
- actualRGB11[n] = actualRGB11[n][::-1]
- # print("实际需要的11组RGB值为 : \n", actualRGB11)
- return actualRGB11
- def showRGBcurve(RGB255):
- """
- 根据计RGB255点绘出R/G/B三条曲线
- :param RGB255: 最终计算而得的RGB255数组(3X256),每行分别对应R/G/B的256个值
- """
- x = np.arange(0, 256)
- plt.title("RGB255")
- # x轴的刻度间隔
- x_major_locator = MultipleLocator(15)
- # y轴的刻度间隔
- y_major_locator = MultipleLocator(10)
- # ax为两条坐标轴的实例
- ax = plt.gca()
- ax.xaxis.set_major_locator(x_major_locator)
- ax.yaxis.set_major_locator(y_major_locator)
- plt.plot(x, RGB255[0], label="R", color='red')
- plt.plot(x, RGB255[1], label="G", color='green')
- plt.plot(x, RGB255[2], label="B", color='blue')
- plt.legend()
- plt.show()
- class RGBcalculator(metaclass=ABCMeta):
- @abstractmethod
- def calRGB255(self, filePath: str):
- pass
- class Firetv_RGBcalculator(RGBcalculator):
- target_x = 0
- target_y = 0
- target_gamma = 2.2
- RGB_2_XYZ = np.zeros((3, 3))
- XYZ_11 = np.zeros((3, 11))
- XYZ11_index = [255, 229, 204, 178, 153, 127, 102, 76, 51, 26, 0]
- def calRGB255(self, FilePath: str):
- # 从excel中读取“转换矩阵”,“11组测量的XYZ”,以及“11组XYZ对应的坐标索引”
- self.RGB_2_XYZ, self.XYZ_11, selfXYZ11_index, self.target_x, \
- self.target_y, self.target_gamma = readDataFromExcel(FilePath)
- print("转换矩阵RGB_to_XYZ为:\n", self.RGB_2_XYZ)
- XYZ_2_RGB = np.linalg.pinv(self.RGB_2_XYZ)
- print("转换逆矩阵XYZ_2_RGB为:\n", XYZ_2_RGB)
- targetXYZ11 = cal_target_XYZ11(XYZ_2_RGB, self.target_x, self.target_y, self.target_gamma)
- print("11个目标target_XYZ值: \n", targetXYZ11)
- # 算出11组目标RGB
- targetRGB11 = cal_RGB_by_XYZ(XYZ_2_RGB, targetXYZ11)
- print("11个目标targetRGB11值: \n", targetRGB11)
- # 对测量出来的XYZ11做插值运算
- XYZ256 = interpolate_to_256(self.XYZ_11, self.XYZ11_index)
- # XYZ256 = interpolate3d_to_256(XYZ_11, XYZ11_index)
- print(f"测量的11个XYZ11为:\n {self.XYZ_11}")
- print(f"对XYZ11插值扩展后为XYZ256:\n {XYZ256}")
- ActualRGB11 = calActualRGB11(targetRGB11, targetXYZ11, XYZ256)
- print("实际需要的11个RGB值: \n", ActualRGB11)
- # 对实际需要的RGB11做插值运算得到最终的RGB256
- ActualRGB256 = interpolate_to_256(ActualRGB11, self.XYZ11_index)
- ActualRGB256 = np.multiply(ActualRGB256, 255) # 去归一化,以RGB-255显示
- ActualRGB256 = np.around(ActualRGB256, 4) # 小数点保留4位
- print(f"最终需要的256组RGB值: \n{ActualRGB256}")
- return ActualRGB256
- def calRGB255_for_Debug(self, new_XYZ_of_RGB, new_XYZ_11, new_XYZ11_index, new_target_x,
- new_target_y, new_target_gamma):
- """
- :param new_XYZ_of_RGB: 3X3矩阵
- :param new_XYZ_11: 3X11矩阵
- :param new_XYZ11_index: 1X11数组
- :param new_target_x:
- :param new_target_y:
- :param new_target_gamma:
- :return:
- """
- self.target_x = new_target_x
- self.target_y = new_target_y
- self.target_gamma = new_target_gamma
- # “转换矩阵”,“11组测量的XYZ”,以及“11组XYZ对应的`Z坐标索引”
- self.RGB_2_XYZ = new_XYZ_of_RGB
- self.XYZ_11 = new_XYZ_11
- self.XYZ11_index = new_XYZ11_index
- print(f"获取到:\ntarget_x = {self.target_x}\ntarget_y = {self.target_y}\ntarget_gamma = {self.target_gamma}\n")
- print("转换矩阵RGB_to_XYZ为:\n", self.RGB_2_XYZ)
- XYZ_2_RGB = np.linalg.pinv(self.RGB_2_XYZ)
- print("转换逆矩阵XYZ_2_RGB为:\n", XYZ_2_RGB)
- targetXYZ11 = cal_target_XYZ11(XYZ_2_RGB, self.target_x, self.target_y, self.target_gamma)
- print("11个目标target_XYZ值: \n", targetXYZ11)
- # 算出11组目标RGB
- targetRGB11 = cal_RGB_by_XYZ(XYZ_2_RGB, targetXYZ11)
- print("11个目标targetRGB11值: \n", targetRGB11)
- # 对测量出来的XYZ11做插值运算
- XYZ256 = interpolate_to_256(self.XYZ_11, self.XYZ11_index)
- # XYZ256 = interpolate3d_to_256(XYZ_11, XYZ11_index)
- print(f"测量的11个XYZ11为:\n {self.XYZ_11}")
- print(f"对XYZ11插值扩展后为XYZ256:\n {XYZ256}")
- ActualRGB11 = calActualRGB11(targetRGB11, targetXYZ11, XYZ256)
- print("实际需要的11个RGB值: \n", ActualRGB11)
- ActualXYZ11 = np.dot(XYZ_2_RGB, ActualRGB11)
- print(f"实际需要的11组XYZ值: \n{ActualXYZ11}")
- # 对实际需要的RGB11做插值运算得到最终的RGB256
- ActualRGB256 = interpolate_to_256(ActualRGB11, self.XYZ11_index)
- # showRGBcurve(np.multiply(ActualRGB256, 255))
- ActualRGB256 = np.multiply(ActualRGB256, 1023) # 去归一化,以RGB-255显示
- ActualRGB256 = np.around(ActualRGB256, 0).astype(int) # 小数点保留4位
- ActualXYZ256 = np.dot(XYZ_2_RGB, ActualRGB256)
- print(f"最终需要的256组XYZ值: \n{ActualXYZ256}")
- print(f"最终需要的256组RGB值: \n{ActualRGB256}")
- return ActualRGB256
- def dumpRGB2ini(RGB255):
- """
- 功能:将最终计算的RGB255值输出至文本,供机芯调用.该格式的ini暂时用不到
- :param RGB255:最终计算的RGB255数组(3X256),每行分别对应R/G/B的256个值
- """
- wb_mode = {}
- list = ["COLD", "STANDARD", "STANDARD2", "WARM", "WARM2", "SDR2HDR COLD", "SDR2HDR STANDARD", "SDR2HDR WARM"]
- for str in list:
- wb_mode[str] = [{"R_VALUE": []}, {"G_VALUE": []}, {"B_VALUE": []}]
- wb_mode[str][0]["R_VALUE"] = RGB255[0, :].tolist()
- wb_mode[str][1]["G_VALUE"] = RGB255[1, :].tolist()
- wb_mode[str][2]["B_VALUE"] = RGB255[2, :].tolist()
- # print("wb_mode: \n", wb_mode)
- options = jsbeautifier.default_options()
- options.indent_size = 2
- json_dict = jsbeautifier.beautify(json.dumps(wb_mode), options)
- print(json_dict)
- filename = 'gamma.ini'
- with open(filename, 'w', encoding='utf-8') as f:
- f.write(json_dict)
- print(f"gamma文件已保存至{filename}")
- def dumpRGB2ini_commom(RGB255):
- """
- 功能:将最终计算的RGB255值按一定格式输出至文本,供机芯调用.该格式比较通用
- :param RGB255: 最终计算的RGB255数组(3X256),每行分别对应R/G/B的256个值
- """
- RGB_list = []
- RGB_reg = "{0x000,0x010,0x020,0x030,0x040,0x050,0x060,0x070,0x080,0x090,0x0A0,0x0B0,0x0C0,0x0D0,0x0E0,0x0F0,\
- 0x100,0x110,0x120,0x130,0x140,0x150,0x160,0x170,0x180,0x190,0x1A0,0x1B0,0x1C0,0x1D0,0x1E0,0x1F0,\
- 0x200,0x210,0x220,0x230,0x240,0x250,0x260,0x270,0x280,0x290,0x2A0,0x2B0,0x2C0,0x2D0,0x2E0,0x2F0,\
- 0x300,0x310,0x320,0x330,0x340,0x350,0x360,0x370,0x380,0x390,0x3A0,0x3B0,0x3C0,0x3D0,0x3E0,0x3F0,\
- 0x400,0x410,0x420,0x430,0x440,0x450,0x460,0x470,0x480,0x490,0x4A0,0x4B0,0x4C0,0x4D0,0x4E0,0x4F0,\
- 0x500,0x510,0x520,0x530,0x540,0x550,0x560,0x570,0x580,0x590,0x5A0,0x5B0,0x5C0,0x5D0,0x5E0,0x5F0,\
- 0x600,0x610,0x620,0x630,0x640,0x650,0x660,0x670,0x680,0x690,0x6A0,0x6B0,0x6C0,0x6D0,0x6E0,0x6F0,\
- 0x700,0x710,0x720,0x730,0x740,0x750,0x760,0x770,0x780,0x790,0x7A0,0x7B0,0x7C0,0x7D0,0x7E0,0x7F0,\
- 0x800,0x810,0x820,0x830,0x840,0x850,0x860,0x870,0x880,0x890,0x8A0,0x8B0,0x8C0,0x8D0,0x8E0,0x8F0,\
- 0x900,0x910,0x920,0x930,0x940,0x950,0x960,0x970,0x980,0x990,0x9A0,0x9B0,0x9C0,0x9D0,0x9E0,0x9F0,\
- 0xA00,0xA10,0xA20,0xA30,0xA40,0xA50,0xA60,0xA70,0xA80,0xA90,0xAA0,0xAB0,0xAC0,0xAD0,0xAE0,0xAF0,\
- 0xB00,0xB10,0xB20,0xB30,0xB40,0xB50,0xB60,0xB70,0xB80,0xB90,0xBA0,0xBB0,0xBC0,0xBD0,0xBE0,0xBF0,\
- 0xC00,0xC10,0xC20,0xC30,0xC40,0xC50,0xC60,0xC70,0xC80,0xC90,0xCA0,0xCB0,0xCC0,0xCD0,0xCE0,0xCF0,\
- 0xD00,0xD10,0xD20,0xD30,0xD40,0xD50,0xD60,0xD70,0xD80,0xD90,0xDA0,0xDB0,0xDC0,0xDD0,0xDE0,0xDF0,\
- 0xE00,0xE10,0xE20,0xE30,0xE40,0xE50,0xE60,0xE70,0xE80,0xE90,0xEA0,0xEB0,0xEC0,0xED0,0xEE0,0xEF0,\
- 0xF00,0xF10,0xF20,0xF30,0xF40,0xF50,0xF60,0xF70,0xF80,0xF90,0xFA0,0xFB0,0xFC0,0xFD0,0xFE0,0xFF0}"
- # print("RGB255:\n", RGB255)
- list = ["R //Gamma - R", "G //Gamma - G", "B //Gamma - B"]
- for i in range(0, 3):
- RGB_list.append(list[i])
- for n in range(0, 256):
- RGB_list.append(RGB255[i][n])
- RGB_list.append("\n")
- # 按照已知gamma.txt样式生成内容
- RGB_list.append("/*")
- RGB_list.append("//Settint R")
- RGB_list.append(RGB_reg)
- RGB_list.append("\n")
- RGB_list.append("//Settint G")
- RGB_list.append(RGB_reg)
- RGB_list.append("\n")
- RGB_list.append("//Settint R")
- RGB_list.append(RGB_reg)
- RGB_list.append("\n")
- RGB_list.append("/*")
- # print("RGB_list: \n", RGB_list)
- time = datetime.strftime(datetime.now(), '%Y%m%d_%H%M%S') #
- filename = 'gamma.ini'
- with open(filename, 'w') as fp:
- [fp.write(str(item) + '\n') for item in RGB_list]
- fp.close()
- print(f"gamma文件已保存至{filename}")
- class Factory(metaclass=ABCMeta):
- @abstractmethod
- def create(self):
- pass
- class FiretvFactory(Factory):
- def __init__(self):
- print("FiretvFactory...")
- def create(self):
- return Firetv_RGBcalculator()
|