|
@@ -0,0 +1,337 @@
|
|
|
+"""
|
|
|
+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()
|