From b6c1c2ab9336c202b1ef306b6d263152d1928756 Mon Sep 17 00:00:00 2001 From: "xinzhu.yin" Date: Mon, 20 Apr 2026 11:13:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=B5=8B=E8=AF=95=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E9=83=A8=E5=88=86+=E5=8E=BB=E9=99=A4=E8=BD=AC?= =?UTF-8?q?=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/config_io.py | 14 +- app/device/connection.py | 24 +- app/plots/plot_accuracy.py | 3 +- app/plots/plot_cct.py | 3 +- app/plots/plot_contrast.py | 3 +- app/plots/plot_eotf.py | 3 +- app/plots/plot_gamma.py | 3 +- app/plots/plot_gamut.py | 3 +- app/runner/__init__.py | 0 app/runner/test_runner.py | 1462 ++++++++++++++++++++++++++++++++ app/tests/local_dimming.py | 21 +- app/views/chart_frame.py | 30 +- pqAutomationApp.py | 1643 ++---------------------------------- settings/pq_config.json | 2 +- 14 files changed, 1583 insertions(+), 1631 deletions(-) create mode 100644 app/runner/__init__.py create mode 100644 app/runner/test_runner.py diff --git a/app/config_io.py b/app/config_io.py index 1bb99bb..45aa3a5 100644 --- a/app/config_io.py +++ b/app/config_io.py @@ -8,11 +8,8 @@ import json import os import sys -def get_config_path(app): +def get_config_path(self): """获取配置文件的完整路径(兼容打包后的程序)""" - self = app - import os - import sys # 判断是否是打包后的程序 if getattr(sys, "frozen", False): @@ -33,9 +30,8 @@ def get_config_path(app): return config_file -def load_pq_config(app): +def load_pq_config(self): """加载PQ配置(兼容打包后的程序)""" - self = app try: # ✅ 使用 self.config_file(已经是动态路径) if os.path.exists(self.config_file): @@ -52,9 +48,8 @@ def load_pq_config(app): self.log_gui.log(f"⚠️ 加载配置文件失败: {str(e)},使用默认配置") -def save_pq_config(app): +def save_pq_config(self): """保存PQ配置(兼容打包后的程序)""" - self = app try: # 确保目录存在 os.makedirs(os.path.dirname(self.config_file), exist_ok=True) @@ -66,9 +61,8 @@ def save_pq_config(app): self.log_gui.log(f"保存配置文件失败: {str(e)}") -def clear_config_file(app): +def clear_config_file(self): """清理配置文件(兼容打包后的程序)""" - self = app from tkinter import messagebox config_file = self.get_config_path() diff --git a/app/device/connection.py b/app/device/connection.py index 37afac0..8536209 100644 --- a/app/device/connection.py +++ b/app/device/connection.py @@ -10,15 +10,13 @@ from tkinter import messagebox from utils.caSerail import CASerail -def get_available_ucd_ports(app): +def get_available_ucd_ports(self): """获取可用的UCD端口列表""" - self = app return self.ucd.search_device() -def get_available_com_ports(app): +def get_available_com_ports(self): """获取可用的COM端口列表""" - self = app try: import serial.tools.list_ports @@ -29,9 +27,8 @@ def get_available_com_ports(app): return [] -def refresh_com_ports(app): +def refresh_com_ports(self): """刷新COM端口列表""" - self = app available_ports = self.get_available_com_ports() available_list = self.get_available_ucd_ports() @@ -60,9 +57,8 @@ def refresh_com_ports(app): self.update_config() -def check_com_connections(app): +def check_com_connections(self): """检测COM端口连接状态""" - self = app # 禁用连接按钮,防止重复点击 self.check_button.configure(state="disabled") self.refresh_button.configure(state="disabled") @@ -105,18 +101,16 @@ def check_com_connections(app): threading.Thread(target=check_connections, daemon=True).start() -def update_connection_indicator(app, indicator, connected): +def update_connection_indicator(self, indicator, connected): """更新连接状态指示器颜色""" - self = app if connected: indicator.config(bg="green") else: indicator.config(bg="red") -def check_port_connection(app, is_ucd=True): +def check_port_connection(self, is_ucd=True): """检测指定端口是否可以连接""" - self = app try: if is_ucd: if self.ucd.status: @@ -166,16 +160,14 @@ def check_port_connection(app, is_ucd=True): return False -def enable_com_widgets(app): +def enable_com_widgets(self): """重新启用所有控件""" - self = app self.check_button.configure(state="normal") self.refresh_button.configure(state="normal") -def disconnect_com_connections(app): +def disconnect_com_connections(self): """断开所有串口连接""" - self = app try: # 断开TV连接 if self.ucd.status: diff --git a/app/plots/plot_accuracy.py b/app/plots/plot_accuracy.py index 44fdbb7..6e2e9ee 100644 --- a/app/plots/plot_accuracy.py +++ b/app/plots/plot_accuracy.py @@ -6,9 +6,8 @@ Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_accuracy 原样搬迁 from matplotlib.patches import Rectangle -def plot_accuracy(app, accuracy_data, test_type): +def plot_accuracy(self, accuracy_data, test_type): """绘制色准测试结果 - 29色显示 - 简洁版布局(显示 Gamma)""" - self = app self.accuracy_ax.clear() self.accuracy_ax.set_xlim(0, 1) diff --git a/app/plots/plot_cct.py b/app/plots/plot_cct.py index e5860e4..282d560 100644 --- a/app/plots/plot_cct.py +++ b/app/plots/plot_cct.py @@ -6,9 +6,8 @@ Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_cct 原样搬迁。 import numpy as np -def plot_cct(app, test_type): +def plot_cct(self, test_type): """绘制 x 和 y 坐标分离图 - 每个点标注纵坐标值""" - self = app self.cct_fig.clear() diff --git a/app/plots/plot_contrast.py b/app/plots/plot_contrast.py index 092e437..5ecbe89 100644 --- a/app/plots/plot_contrast.py +++ b/app/plots/plot_contrast.py @@ -6,9 +6,8 @@ Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_contrast 原样搬迁 from matplotlib.patches import Rectangle -def plot_contrast(app, contrast_data, test_type): +def plot_contrast(self, contrast_data, test_type): """绘制对比度测试结果 - 固定布局版本""" - self = app # 清空并重置 self.contrast_ax.clear() diff --git a/app/plots/plot_eotf.py b/app/plots/plot_eotf.py index ee1cd6f..c1ebed3 100644 --- a/app/plots/plot_eotf.py +++ b/app/plots/plot_eotf.py @@ -6,9 +6,8 @@ Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_eotf 原样搬迁。 import numpy as np -def plot_eotf(app, L_bar, results_with_eotf_list, test_type): +def plot_eotf(self, L_bar, results_with_eotf_list, test_type): """绘制 EOTF 曲线 + 数据表格(HDR 专用,包含实测亮度)""" - self = app # ========== 1. 清空并重置左侧曲线 ========== self.eotf_ax.clear() diff --git a/app/plots/plot_gamma.py b/app/plots/plot_gamma.py index df1c25b..642b5be 100644 --- a/app/plots/plot_gamma.py +++ b/app/plots/plot_gamma.py @@ -6,9 +6,8 @@ Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_gamma 原样搬迁。 import numpy as np -def plot_gamma(app, L_bar, results_with_gamma_list, target_gamma, test_type): +def plot_gamma(self, L_bar, results_with_gamma_list, target_gamma, test_type): """绘制Gamma曲线 + 数据表格(包含实测亮度)""" - self = app # ========== 1. 清空并重置左侧曲线 ========== self.gamma_ax.clear() diff --git a/app/plots/plot_gamut.py b/app/plots/plot_gamut.py index 13b01fe..07cbafe 100644 --- a/app/plots/plot_gamut.py +++ b/app/plots/plot_gamut.py @@ -10,11 +10,10 @@ import algorithm.pq_algorithm as pq_algorithm from app.resources import get_resource_path -def plot_gamut(app, results, coverage, test_type): +def plot_gamut(self, results, coverage, test_type): """绘制色域图 - 根据用户选择的参考标准动态计算覆盖率""" # 实现从原 PQAutomationApp 方法体原样搬迁,为减少修改面 # 范围、保持行为一致,给 self 赋值为传入的 app 实例。 - self = app self.gamut_ax_xy.clear() self.gamut_ax_uv.clear() diff --git a/app/runner/__init__.py b/app/runner/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/runner/test_runner.py b/app/runner/test_runner.py new file mode 100644 index 0000000..7548d28 --- /dev/null +++ b/app/runner/test_runner.py @@ -0,0 +1,1462 @@ +"""测试执行(runner)相关逻辑(Step 5 重构)。 + +从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app` +以保留原有 `self.xxx` 属性访问不变。 +""" + +import datetime +import time +import traceback +from tkinter import messagebox +import tkinter as tk + +import colour +import numpy as np + +import algorithm.pq_algorithm as pq_algorithm +from utils.data_range_converter import convert_pattern_params +from utils.pq.pq_result import PQResult + +def new_pq_results(self, test_type, test_name): + self.results = PQResult(test_type, test_name) + # 设置配置 + config = { + "test_type": test_type, + "test_name": test_name, + "test_items": self.config.current_test_types[test_type]["test_items"], + "test_items_chinese": self.config.get_test_item_chinese_names( + self.config.current_test_types[test_type]["test_items"] + ), + } + self.results.set_test_config(config) + + # 添加测试项 + for item in config["test_items"]: + self.results.add_test_item( + item, config["test_items_chinese"][config["test_items"].index(item)] + ) + + +def run_test(self, test_type, test_items): + """执行测试""" + try: + self.log_gui.log(f"开始执行{self.get_test_type_name(test_type)}测试") + self.log_gui.log( + f"测试项目: {', '.join(self.config.get_test_item_chinese_names(test_items))}" + ) + + # 根据测试类型执行不同的测试流程 + if test_type == "screen_module": + self.run_screen_module_test(test_items) + elif test_type == "sdr_movie": + self.run_sdr_movie_test(test_items) + elif test_type == "hdr_movie": + self.run_hdr_movie_test(test_items) + + # 测试完成后更新UI状态 + if self.testing: # 如果没有被中途停止 + self.root.after(0, self.on_test_completed) + except Exception as e: + self.log_gui.log(f"测试过程中发生错误: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + self.root.after(0, self.on_test_error) + + +def run_screen_module_test(self, test_items): + """执行屏模组性能测试 - 优化版""" + self.log_gui.log("执行屏模组性能测试...") + + if test_items: + self.new_pq_results("screen_module", "屏模组性能测试") + else: + self.log_gui.log("未选择任何测试项目") + return + + # 判断是否需要灰阶数据 + needs_gray_data = any( + item in test_items for item in ["gamma", "cct", "contrast"] + ) + shared_gray_data = None # 共享的灰阶数据 + + # 计算总测试项数量 + total_items = len(test_items) + current_item = 0 + + for item in test_items: + if not self.testing: # 检查是否被停止 + return + + current_item += 1 + self.status_var.set(f"测试进行中... ({current_item}/{total_items})") + + # ==================== 色域测试 ==================== + if item == "gamut": + self.test_gamut("screen_module") + + # ==================== 灰阶数据采集 ==================== + # 如果是第一个需要灰阶数据的测试项,统一采集数据 + elif ( + item in ["gamma", "cct", "contrast"] + and shared_gray_data is None + and needs_gray_data + ): + self.log_gui.log("=" * 50) + self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") + self.log_gui.log("=" * 50) + + shared_gray_data = self.send_fix_pattern("gray") + + if not shared_gray_data or len(shared_gray_data) < 2: + self.log_gui.log("灰阶数据采集失败或数据不足,跳过相关测试") + return + + self.log_gui.log( + f"✓ 灰阶数据采集完成,共 {len(shared_gray_data)} 个数据点" + ) + + # 保存到 results 对象,供所有灰阶测试使用 + self.results.add_intermediate_data("shared", "gray", shared_gray_data) + + # 执行当前测试项 + if item == "gamma": + self.test_gamma("screen_module", shared_gray_data) + elif item == "cct": + self.test_cct("screen_module", shared_gray_data) + elif item == "contrast": + self.test_contrast("screen_module", shared_gray_data) + + # ==================== 后续灰阶测试(复用数据) ==================== + elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: + self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") + + if item == "gamma": + self.test_gamma("screen_module", shared_gray_data) + elif item == "cct": + self.test_cct("screen_module", shared_gray_data) + elif item == "contrast": + self.test_contrast("screen_module", shared_gray_data) + + +def run_custom_sdr_test(self, test_items): + """执行客户定制 SDR 测试 - 升级版""" + self.log_gui.log("执行客户定制 SDR 测试...") + # 获取信号格式设置 + color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 + gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6 + data_range = self.sdr_data_range_var.get() # Full/Limited + bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit + + self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}") + self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") + self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") + self.test_custom_sdr() + + if self.testing: + self.root.after(0, self.on_custom_template_test_completed) + + +def run_sdr_movie_test(self, test_items): + """执行SDR Movie测试""" + self.log_gui.log("执行SDR Movie测试...") + + if test_items: + self.new_pq_results("sdr_movie", "SDR Movie测试") + else: + self.log_gui.log("未选择任何测试项目") + return + + # 获取信号格式设置 + color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 + gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6 + data_range = self.sdr_data_range_var.get() # Full/Limited + bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit + + self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}") + self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") + + # 判断是否需要灰阶数据 + needs_gray_data = any( + item in test_items for item in ["gamma", "cct", "contrast"] + ) + shared_gray_data = None + + # 计算总测试项数量 + total_items = len(test_items) + current_item = 0 + + for item in test_items: + if not self.testing: + return + + current_item += 1 + self.status_var.set(f"测试进行中... ({current_item}/{total_items})") + + if item == "gamut": + self.test_gamut("sdr_movie") + + elif ( + item in ["gamma", "cct", "contrast"] + and shared_gray_data is None + and needs_gray_data + ): + self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") + shared_gray_data = self.send_fix_pattern("gray") + + if not shared_gray_data or len(shared_gray_data) < 2: + self.log_gui.log("灰阶数据采集失败或数据不足") + return + + self.results.add_intermediate_data("shared", "gray", shared_gray_data) + + if item == "gamma": + self.test_gamma("sdr_movie", shared_gray_data) + elif item == "cct": + self.test_cct("sdr_movie", shared_gray_data) + elif item == "contrast": + self.test_contrast("sdr_movie", shared_gray_data) + + elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: + self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") + if item == "gamma": + self.test_gamma("sdr_movie", shared_gray_data) + elif item == "cct": + self.test_cct("sdr_movie", shared_gray_data) + elif item == "contrast": + self.test_contrast("sdr_movie", shared_gray_data) + + elif item == "accuracy": + self.test_color_accuracy("sdr_movie") + + +def run_hdr_movie_test(self, test_items): + """执行HDR Movie测试""" + self.log_gui.log("执行HDR Movie测试...") + + if test_items: + self.new_pq_results("hdr_movie", "HDR Movie测试") + else: + self.log_gui.log("未选择任何测试项目") + return + + # 获取信号格式设置 + color_space = self.hdr_color_space_var.get() + max_cll = self.hdr_maxcll_var.get() + max_fall = self.hdr_maxfall_var.get() + data_range = self.hdr_data_range_var.get() + bit_depth = self.hdr_bit_depth_var.get() + + self.log_gui.log(f"信号格式: 色彩空间={color_space}") + self.log_gui.log(f" MaxCLL={max_cll}, MaxFALL={max_fall}") + self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") + + # 判断是否需要灰阶数据 + needs_gray_data = any( + item in test_items for item in ["eotf", "cct", "contrast"] + ) + shared_gray_data = None + + # 计算总测试项数量 + total_items = len(test_items) + current_item = 0 + + for item in test_items: + if not self.testing: + return + + current_item += 1 + self.status_var.set(f"测试进行中... ({current_item}/{total_items})") + + if item == "gamut": + self.test_gamut("hdr_movie") + + elif ( + item in ["eotf", "cct", "contrast"] + and shared_gray_data is None + and needs_gray_data + ): + self.log_gui.log("开始统一采集灰阶数据(用于 EOTF/CCT/对比度测试)") + shared_gray_data = self.send_fix_pattern("gray") + + if not shared_gray_data or len(shared_gray_data) < 2: + self.log_gui.log("灰阶数据采集失败或数据不足") + return + + self.results.add_intermediate_data("shared", "gray", shared_gray_data) + + if item == "eotf": + self.test_eotf("hdr_movie", shared_gray_data) + elif item == "cct": + self.test_cct("hdr_movie", shared_gray_data) + elif item == "contrast": + self.test_contrast("hdr_movie", shared_gray_data) + + elif item in ["eotf", "cct", "contrast"] and shared_gray_data is not None: + self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") + if item == "eotf": + self.test_eotf("hdr_movie", shared_gray_data) + elif item == "cct": + self.test_cct("hdr_movie", shared_gray_data) + elif item == "contrast": + self.test_contrast("hdr_movie", shared_gray_data) + + elif item == "accuracy": + self.test_color_accuracy("hdr_movie") + + +def send_fix_pattern(self, mode): + """发送固定图案并采集数据 - 支持不同测试类型的信号格式""" + results = [] + + try: + # 1. 设置图案模式 + if mode == "rgb": + self.config.set_current_pattern("rgb") + elif mode == "gray": + self.config.set_current_pattern("gray") + elif mode == "accuracy": # 色准模式(SDR 和 HDR 通用 29色) + self.config.set_current_pattern("accuracy") + elif mode == "custom": + self.config.set_current_pattern("custom") + else: + self.log_gui.log(f"❌ 未知的图案模式: {mode}") + return None + + # 2. 获取当前测试类型 + test_type = self.config.current_test_type + + # 3. 根据测试类型设置信号格式和图案 + if test_type == "screen_module": + # 屏模组测试:使用 Timing + self.log_gui.log("=" * 50) + self.log_gui.log("设置屏模组信号格式:") + self.log_gui.log("=" * 50) + + timing_str = self.config.current_test_types[test_type]["timing"] + self.log_gui.log(f" Timing: {timing_str}") + + # ✅ 屏模组测试:直接使用原始配置 + self.ucd.set_ucd_params(self.config) + + elif test_type == "sdr_movie": + # SDR 测试:设置色彩空间、Gamma 等 + self.log_gui.log("=" * 50) + self.log_gui.log("设置 SDR 信号格式:") + self.log_gui.log("=" * 50) + + color_space = self.sdr_color_space_var.get() + gamma = self.sdr_gamma_type_var.get() + data_range = self.sdr_data_range_var.get() + bit_depth = self.sdr_bit_depth_var.get() + + self.log_gui.log(f" 色彩空间: {color_space}") + self.log_gui.log(f" Gamma: {gamma}") + self.log_gui.log(f" 数据范围: {data_range}") + self.log_gui.log(f" 编码位深: {bit_depth}") + + success = self.ucd.set_sdr_format( + color_space=color_space, + gamma=gamma, + data_range=data_range, + bit_depth=bit_depth, + ) + + if success: + self.log_gui.log("✓ SDR 信号格式设置成功") + else: + self.log_gui.log("✗ SDR 信号格式设置失败") + + # 设置图案参数 + if mode == "accuracy": + self.log_gui.log(f"设置 SDR 29色色准测试图案...") + else: + self.log_gui.log(f"设置 SDR 测试图案({mode} 模式)...") + + # ========== ✅✅✅ 修改:使用临时配置对象 ========== + import copy + + # 从原始配置获取参数(每次都是干净的) + if mode == "rgb": + original_params = copy.deepcopy( + self.config.default_pattern_rgb["pattern_params"] + ) + elif mode == "gray": + original_params = copy.deepcopy( + self.config.default_pattern_gray["pattern_params"] + ) + elif mode == "accuracy": + original_params = copy.deepcopy( + self.config.default_pattern_accuracy["pattern_params"] + ) + elif mode == "custom": + original_params = copy.deepcopy( + self.config.default_pattern_temp["pattern_params"] + ) + + self.log_gui.log(f"🔍 使用原始 RGB 参数(前 3 个):") + for i in range(min(3, len(original_params))): + self.log_gui.log(f" [{i+1}] {original_params[i]}") + + # 根据 data_range 转换 + converted_params = convert_pattern_params( + pattern_params=original_params, data_range=data_range, verbose=False + ) + + if data_range == "Limited": + self.log_gui.log("🔧 转换为 Limited Range (16-235):") + for i in range(min(3, len(converted_params))): + self.log_gui.log( + f" {original_params[i]} → {converted_params[i]}" + ) + else: + self.log_gui.log("✓ Full Range,RGB 保持不变") + + # ✅ 创建临时配置对象(不修改 self.config) + temp_config = self.config.get_temp_config_with_converted_params( + mode=mode, converted_params=converted_params + ) + + # ✅ 使用临时配置设置参数 + self.ucd.set_ucd_params(temp_config) + + self.log_gui.log(f"✓ 图案参数已设置,共 {len(converted_params)} 个图案") + # ========== 修改结束 ========== + + elif test_type == "hdr_movie": + # HDR 测试:设置色彩空间、Metadata 等 + self.log_gui.log("=" * 50) + self.log_gui.log("设置 HDR 信号格式:") + self.log_gui.log("=" * 50) + + color_space = self.hdr_color_space_var.get() + data_range = self.hdr_data_range_var.get() + bit_depth = self.hdr_bit_depth_var.get() + max_cll = self.hdr_maxcll_var.get() + max_fall = self.hdr_maxfall_var.get() + + self.log_gui.log(f" 色彩空间: {color_space}") + self.log_gui.log(f" 数据范围: {data_range}") + self.log_gui.log(f" 编码位深: {bit_depth}") + self.log_gui.log(f" MaxCLL: {max_cll}") + self.log_gui.log(f" MaxFALL: {max_fall}") + + success = self.ucd.set_hdr_format( + color_space=color_space, + data_range=data_range, + bit_depth=bit_depth, + max_cll=max_cll, + max_fall=max_fall, + ) + + if success: + self.log_gui.log("✓ HDR 信号格式设置成功") + else: + self.log_gui.log("✗ HDR 信号格式设置失败") + + # 设置图案参数 + if mode == "accuracy": + self.log_gui.log(f"设置 HDR 29色色准测试图案...") + else: + self.log_gui.log(f"设置 HDR 测试图案({mode} 模式)...") + + # ========== ✅✅✅ 修改:使用临时配置对象 ========== + import copy + + # 从原始配置获取参数 + if mode == "rgb": + original_params = copy.deepcopy( + self.config.default_pattern_rgb["pattern_params"] + ) + elif mode == "gray": + original_params = copy.deepcopy( + self.config.default_pattern_gray["pattern_params"] + ) + elif mode == "accuracy": + original_params = copy.deepcopy( + self.config.default_pattern_accuracy["pattern_params"] + ) + + self.log_gui.log(f"🔍 使用原始 RGB 参数(前 3 个):") + for i in range(min(3, len(original_params))): + self.log_gui.log(f" [{i+1}] {original_params[i]}") + + # 根据 data_range 转换 + converted_params = convert_pattern_params( + pattern_params=original_params, data_range=data_range, verbose=False + ) + + if data_range == "Limited": + self.log_gui.log("🔧 转换为 Limited Range (16-235):") + for i in range(min(3, len(converted_params))): + self.log_gui.log( + f" {original_params[i]} → {converted_params[i]}" + ) + else: + self.log_gui.log("✓ Full Range,RGB 保持不变") + + # ✅ 创建临时配置对象 + temp_config = self.config.get_temp_config_with_converted_params( + mode=mode, converted_params=converted_params + ) + + self.ucd.set_ucd_params(temp_config) + + self.log_gui.log(f"✓ 图案参数已设置,共 {len(converted_params)} 个图案") + # ========== 修改结束 ========== + + self.log_gui.log("=" * 50) + + # 4. 循环发送图案并采集数据(使用原始配置的数量) + total_patterns = len(self.config.current_pattern["pattern_params"]) + self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案") + settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0))) + progress_step = max( + 1, int(getattr(self, "pattern_progress_log_step", 5)) + ) + self.log_gui.log( + f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)" + ) + + # 获取颜色名称列表(用于日志显示) + color_names = None + if mode == "accuracy": + color_names = self.config.get_accuracy_color_names() + + custom_pattern_names = [] + if mode == "custom" and hasattr(self.config, "get_temp_pattern_names"): + custom_pattern_names = self.config.get_temp_pattern_names() + + for i in range(total_patterns): + if not self.testing: + self.log_gui.log("⚠️ 测试已停止") + return results + + should_log_detail = ( + i == 0 + or (i + 1) == total_patterns + or ((i + 1) % progress_step == 0) + ) + + # 设置下一个图案(显示颜色名称) + if should_log_detail: + if color_names and i < len(color_names): + self.log_gui.log( + f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..." + ) + else: + self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...") + + self.ucd.set_next_pattern() + self.ucd.run() + time.sleep(settle_time) + + # 测量数据 + if mode == "custom": + result = [] + self.ca.set_Display(1) + tcp, duv, lv, X, Y, Z = self.ca.readAllDisplay() + + if should_log_detail: + self.log_gui.log( + f" ✓ 测量完成: TCP={tcp:.4f}, DUV={duv:.4f}, lv={lv:.2f}, " + f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" + ) + + self.ca.set_Display(8) + lambda_, Pe, lv, X, Y, Z = self.ca.readAllDisplay() + + if should_log_detail: + self.log_gui.log( + f" ✓ 测量完成: λ={lambda_:.4f}, Pe={Pe:.4f}, lv={lv:.2f}, " + f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" + ) + result = [tcp, duv, lv, lambda_, Pe, lv, X, Y, Z] + results.append(result) + + # 每完成一个 pattern,实时写入客户模板结果表。 + try: + xy = colour.XYZ_to_xy(np.array([X, Y, Z])) + u_prime, v_prime, _ = colour.XYZ_to_CIE1976UCS( + np.array([X, Y, Z]) + ) + row_data = { + "pattern_name": ( + custom_pattern_names[i] + if i < len(custom_pattern_names) + else f"P {i + 1}" + ), + "X": X, + "Y": Y, + "Z": Z, + "x": xy[0], + "y": xy[1], + "Lv": lv, + "u_prime": u_prime, + "v_prime": v_prime, + "Tcp": tcp, + "duv": duv, + "lambda_d": lambda_, + "Pe": Pe, + } + self.root.after( + 0, + lambda row_no=i + 1, data=row_data: self.append_custom_template_result( + row_no, data + ), + ) + except Exception as e: + self.log_gui.log(f"⚠️ 第 {i+1} 行实时结果写入失败: {str(e)}") + + else: + self.ca.set_xyLv_Display() + + x, y, lv, X, Y, Z = self.ca.readAllDisplay() + results.append([x, y, lv, X, Y, Z]) + + if should_log_detail: + self.log_gui.log( + f" ✓ 测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " + f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" + ) + + self.log_gui.log(f"✓ 数据采集完成,共 {len(results)} 组数据") + return results + + except Exception as e: + self.log_gui.log(f"❌ 发送图案失败: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + return None + + +def test_custom_sdr(self): + """执行客户定制 SDR 测试 - 升级版""" + self.log_gui.log("执行客户定制 SDR 测试...") + results = self.send_fix_pattern("custom") + if not results: + self.log_gui.log("客户模板SDR测试被中断") + return + + self.log_gui.log(f"客户模板采集完成,共 {len(results)} 组数据") + + +def test_gamut(self, test_type): + """测试色域""" + self.log_gui.log("开始测试色域...") + self.results.start_test_item("gamut") + + try: + # 存储测量结果 + results = self.send_fix_pattern("rgb") + + # 检查结果是否为空 + if not results: + self.log_gui.log("色域测试被中断") + return + + self.results.add_intermediate_data("gamut", "rgb", results) + + # 计算色域覆盖率 + self.log_gui.log("计算色域覆盖率...") + + # 提取 x, y 坐标用于计算色域 + xy_points = [[result[0], result[1]] for result in results] + + # ========== ✅ 测试时:使用色彩空间的值作为参考标准 ========== + reference_standard = None + area = None + coverage = None + + if test_type == "screen_module": + # 屏模组测试:固定使用 DCI-P3(因为没有色彩空间设置) + reference_standard = "DCI-P3" + + # ✅ 同步更新到色域参考标准变量(供后续重绘使用) + self.screen_gamut_ref_var.set(reference_standard) + + elif test_type == "sdr_movie": + # SDR 测试:使用色彩空间设置 + color_space = self.sdr_color_space_var.get() + + if color_space == "BT.709": + reference_standard = "BT.709" + elif color_space == "BT.601": + reference_standard = "BT.601" + elif color_space == "BT.2020": + reference_standard = "BT.2020" + else: + reference_standard = "BT.709" + self.log_gui.log( + f"⚠️ 未识别的色彩空间 '{color_space}',使用默认标准 BT.709" + ) + + # ✅ 同步更新到色域参考标准变量 + self.sdr_gamut_ref_var.set(reference_standard) + + elif test_type == "hdr_movie": + # HDR 测试:使用色彩空间设置 + color_space = self.hdr_color_space_var.get() + + if color_space == "BT.2020": + reference_standard = "BT.2020" + elif color_space == "DCI-P3": + reference_standard = "DCI-P3" + else: + reference_standard = "BT.2020" + self.log_gui.log( + f"⚠️ 未识别的色彩空间 '{color_space}',使用默认标准 BT.2020" + ) + + # ✅ 同步更新到色域参考标准变量 + self.hdr_gamut_ref_var.set(reference_standard) + else: + # 未知测试类型,使用 DCI-P3 作为后备 + reference_standard = "DCI-P3" + self.log_gui.log( + f"⚠️ 未识别的测试类型 '{test_type}',使用默认标准 DCI-P3" + ) + + # ========== 根据参考标准计算 XY 覆盖率 ========== + if reference_standard == "BT.2020": + area, coverage = pq_algorithm.calculate_gamut_coverage_BT2020(xy_points) + elif reference_standard == "BT.709": + area, coverage = pq_algorithm.calculate_gamut_coverage_BT709(xy_points) + elif reference_standard == "DCI-P3": + area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) + elif reference_standard == "BT.601": + area, coverage = pq_algorithm.calculate_gamut_coverage_BT601(xy_points) + else: + # 默认使用 DCI-P3 + area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) + reference_standard = "DCI-P3" + self.log_gui.log( + f"⚠️ 未识别的参考标准 '{reference_standard}',使用默认标准 DCI-P3" + ) + + # ========== ✅✅✅ 新增:计算 UV 覆盖率 ========== + uv_coverage = 0 + try: + # 将 XY 转换为 UV + uv_points = [] + for x, y in xy_points: + u, v = pq_algorithm.xy_to_uv_1976(x, y) + uv_points.append([u, v]) + + # 根据参考标准计算 UV 覆盖率 + if len(uv_points) >= 3: + if reference_standard == "BT.2020": + _, uv_coverage = ( + pq_algorithm.calculate_gamut_coverage_BT2020_uv(uv_points) + ) + elif reference_standard == "BT.709": + _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT709_uv( + uv_points + ) + elif reference_standard == "DCI-P3": + _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv( + uv_points + ) + elif reference_standard == "BT.601": + _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT601_uv( + uv_points + ) + else: + _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv( + uv_points + ) + + self.log_gui.log( + f"✓ XY 覆盖率: {coverage:.1f}% | UV 覆盖率: {uv_coverage:.1f}%" + ) + except: + uv_coverage = 0 + + # ========== 保存结果时包含 XY 和 UV 覆盖率 ========== + self.results.set_test_item_result( + "gamut", + { + "area": area, + "coverage": coverage, + "uv_coverage": uv_coverage, # ✅ 新增 UV 覆盖率 + "reference": reference_standard, + }, + ) + + # 传递完整的 results 用于绘图 + self.plot_gamut(results, coverage, test_type) + + self.log_gui.log("色域测试完成") + + except Exception as e: + self.log_gui.log(f"色域测试失败: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + raise + + +def test_gamma(self, test_type, gray_data=None): + """测试Gamma曲线 + + Args: + test_type: 测阶数据,如果提供则使用,否则重新采集 + """ + self.log_gui.log("开始测试Gamma曲线...") + self.results.start_test_item("gamma") + + try: + # 使用传入的灰阶数据或独立采集 + if gray_data is not None: + self.log_gui.log("使用共享的灰阶数据") + results = gray_data + else: + self.log_gui.log("独立采集灰阶数据") + results = self.send_fix_pattern("gray") + + if not results or len(results) < 2: + self.log_gui.log("Gamma测试被中断或数据不足") + return + + self.results.add_intermediate_data("gamma", "gray", results) + + self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算") + self.log_gui.log("计算Gamma值...") + + # ========== ✅ 修复:正确获取 max_index_fix ========== + # 获取配置中的值 + config_max_value = self.config.current_pattern.get( + "measurement_max_value", 10 + ) + + # 强制转换为整数 + try: + max_index_fix = int(config_max_value) + except (ValueError, TypeError): + self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10") + max_index_fix = 10 + + self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}") + + # 关键修复:验证并调整 max_index_fix + # max_index_fix 应该是数据点的最大索引(从0开始,所以是 len - 1) + actual_max_index = len(results) - 1 + + if max_index_fix > actual_max_index: + self.log_gui.log( + f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})" + ) + self.log_gui.log(f"自动调整为: {actual_max_index}") + max_index_fix = actual_max_index + + self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}") + # ======================================================== + + # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) + pattern_params = self.config.default_pattern_gray.get( + "pattern_params", None + ) + + # 计算Gamma值(使用修正后的 max_index_fix 和 8bit pattern参数) + results_with_gamma_list, L_bar = self.calculate_gamma( + results, max_index_fix, pattern_params + ) + self.results.set_test_item_result( + "gamma", {"gamma": results_with_gamma_list, "L_bar": L_bar} + ) + + # 绘制Gamma曲线 + if test_type == "sdr_movie": + try: + target_gamma = float(self.sdr_gamma_type_var.get()) + except (ValueError, AttributeError): + target_gamma = 2.2 + else: + target_gamma = 2.2 + + self.plot_gamma(L_bar, results_with_gamma_list, target_gamma, test_type) + + self.log_gui.log("Gamma测试完成") + + except Exception as e: + self.log_gui.log(f"Gamma测试失败: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + raise + + +def test_eotf(self, test_type, gray_data=None): + """测试 EOTF 曲线(HDR 专用) + + Args: + test_type: 测试类型阶数据,如果提供则使用,否则重新采集 + """ + self.log_gui.log("开始测试 EOTF 曲线(HDR)...") + self.results.start_test_item("eotf") + + try: + # 使用传入的灰阶数据或独立采集 + if gray_data is not None: + self.log_gui.log("使用共享的灰阶数据") + results = gray_data + else: + self.log_gui.log("独立采集灰阶数据") + results = self.send_fix_pattern("gray") + + if not results or len(results) < 2: + self.log_gui.log("EOTF 测试被中断或数据不足") + return + + self.results.add_intermediate_data("eotf", "gray", results) + + self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算") + self.log_gui.log("计算 EOTF 值...") + + # ========== 获取 max_index_fix ========== + config_max_value = self.config.current_pattern.get( + "measurement_max_value", 10 + ) + + try: + max_index_fix = int(config_max_value) + except (ValueError, TypeError): + self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10") + max_index_fix = 10 + + self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}") + + # 验证并调整 max_index_fix + actual_max_index = len(results) - 1 + + if max_index_fix > actual_max_index: + self.log_gui.log( + f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})" + ) + self.log_gui.log(f"自动调整为: {actual_max_index}") + max_index_fix = actual_max_index + + self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}") + + # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) + pattern_params = self.config.default_pattern_gray.get( + "pattern_params", None + ) + + # ========== 计算 EOTF(复用 Gamma 计算逻辑,使用8bit pattern参数)========== + results_with_eotf_list, L_bar = self.calculate_gamma( + results, max_index_fix, pattern_params + ) + + # 保存结果 + self.results.set_test_item_result( + "eotf", {"eotf": results_with_eotf_list, "L_bar": L_bar} + ) + + # ========== 绘制 EOTF 曲线 ========== + # HDR 使用 PQ 曲线,目标 gamma 设为 None(不使用传统 gamma) + self.plot_eotf(L_bar, results_with_eotf_list, test_type) + + self.log_gui.log("EOTF 测试完成") + + except Exception as e: + self.log_gui.log(f"EOTF 测试失败: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + raise + + +def test_cct(self, test_type, gray_data=None): + """测试色度一致性""" + self.log_gui.log("开始测试色度一致性...") + self.results.start_test_item("cct") + + try: + if gray_data is not None: + self.log_gui.log("使用共享的灰阶数据") + results = gray_data + else: + self.log_gui.log("独立采集灰阶数据") + results = self.send_fix_pattern("gray") + + if not results: + self.log_gui.log("色度一致性测试被中断") + return + + self.results.add_intermediate_data("cct", "gray", results) + + self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算") + + # 提取色度坐标 + cct_values = pq_algorithm.calculate_cct_from_results(results) + + # 保存到结果 + self.results.set_test_item_result("cct", {"cct_values": cct_values}) + + # 绘制图表 + self.plot_cct(test_type) + + self.log_gui.log("色度一致性测试完成") + except Exception as e: + self.log_gui.log(f"色度一致性测试失败: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + raise + + +def test_contrast(self, test_type, gray_data=None): + """测试对比度 + + Args: + test_type: 阶数据,如果提供则使用,否则重新采集 + """ + self.log_gui.log("开始测试对比度...") + self.results.start_test_item("contrast") + + try: + # ✅ 优先使用传入的灰阶数据 + if gray_data is not None: + self.log_gui.log("使用共享的灰阶数据") + results = gray_data + else: + self.log_gui.log("独立采集灰阶数据") + results = self.send_fix_pattern("gray") + + if not results: + self.log_gui.log("对比度测试被中断") + return + + self.results.add_intermediate_data("contrast", "gray", results) + + # 获取最亮和最暗的亮度值 + luminance_values = [result[2] for result in results] # 提取lv值 + + max_luminance = max(luminance_values) # 最大亮度(白) + min_luminance = min(luminance_values) # 最小亮度(黑) + + # 防止除以0 + if min_luminance < 0.001: + min_luminance = 0.001 + + # 计算对比度 + contrast_ratio = max_luminance / min_luminance + + # 保存结果 + contrast_data = { + "max_luminance": max_luminance, + "min_luminance": min_luminance, + "contrast_ratio": contrast_ratio, + "luminance_values": luminance_values, + } + self.results.set_test_item_result("contrast", contrast_data) + + # 显示对比度结果到日志 + self.log_gui.log(f"最大亮度 (白场): {max_luminance:.2f} cd/m²") + self.log_gui.log(f"最小亮度 (黑场): {min_luminance:.4f} cd/m²") + self.log_gui.log(f"对比度: {contrast_ratio:.0f}:1") + + # 绘制对比度图表 + self.plot_contrast(contrast_data, test_type) + + self.log_gui.log("对比度测试完成") + except Exception as e: + self.log_gui.log(f"对比度测试失败: {str(e)}") + import traceback + + self.log_gui.log(traceback.format_exc()) + raise + + +def test_color_accuracy(self, test_type): + """测试色准 - 使用手工实现的 ΔE 2000(应用 Gamma)""" + + # ========== 读取用户选择的 Gamma ========== + if test_type == "sdr_movie": + try: + target_gamma = float(self.sdr_gamma_type_var.get()) + except (ValueError, AttributeError): + target_gamma = 2.2 + + self.log_gui.log("=" * 50) + self.log_gui.log(f"开始测试色准(SDR Movie 标准 - 29色)") + self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}") # ← 新增 + self.log_gui.log("=" * 50) + + elif test_type == "hdr_movie": + target_gamma = 2.4 # HDR 使用 PQ,但保留参考值 + + self.log_gui.log("=" * 50) + self.log_gui.log(f"开始测试色准(HDR Movie 标准 - 29色)") + self.log_gui.log(f"✓ 使用 Gamma: PQ (参考γ={target_gamma})") # ← 新增 + self.log_gui.log("=" * 50) + + else: # screen_module + target_gamma = 2.2 + + self.log_gui.log("=" * 50) + self.log_gui.log(f"开始测试色准(屏模组 标准 - 29色)") + self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}") + self.log_gui.log("=" * 50) + + # 获取 29色名称 + color_names = self.config.get_accuracy_color_names() + + self.log_gui.log(f"✓ 将测试 {len(color_names)} 个色块") + self.log_gui.log(f" 色块分组:") + self.log_gui.log(f" 灰阶 (5个): {', '.join(color_names[:5])}") + self.log_gui.log(f" ColorChecker (18个): {', '.join(color_names[5:23])}") + self.log_gui.log(f" 饱和色 (6个): {', '.join(color_names[23:])}") + + self.log_gui.log("=" * 50) + self.log_gui.log("开始发送色准图案并采集数据...") + self.log_gui.log("=" * 50) + + # 发送 29色图案 + measured_data_list = self.send_fix_pattern("accuracy") + + if measured_data_list is None or len(measured_data_list) != 29: + self.log_gui.log(f"❌ 数据数量不匹配") + self.log_gui.log(f" 期望: 29 个") + self.log_gui.log( + f" 实际: {len(measured_data_list) if measured_data_list else 0} 个" + ) + return + + # 保存原始测量数据供单步调试使用 + self.results.add_intermediate_data("accuracy", "measured", measured_data_list) + + # ========== 计算 ΔE 2000(显示 Gamma)========== + self.log_gui.log("=" * 50) + self.log_gui.log(f"计算色准(ΔE 2000,Gamma {target_gamma})...") + self.log_gui.log("=" * 50) + + # 获取标准 xy 坐标 + standards = self.get_accuracy_color_standards(test_type) + + delta_e_values = [] + color_patches = [] + + for i, (name, measured_data) in enumerate(zip(color_names, measured_data_list)): + measured_x = measured_data[0] + measured_y = measured_data[1] + measured_lv = measured_data[2] + + standard_x, standard_y = standards.get(name, (0.3127, 0.3290)) + + delta_e = self.calculate_delta_e_2000( + measured_x, + measured_y, + measured_lv, + standard_x, + standard_y, + ) + + delta_e_values.append(delta_e) + color_patches.append(name) + + if delta_e < 3: + grade, icon = "优秀", "✓" + elif delta_e < 5: + grade, icon = "良好", "○" + else: + grade, icon = "偏差", "✗" + + self.log_gui.log( + f" [{i+1:2d}] {name:20s} ΔE={delta_e:5.2f} {icon} {grade}" + ) + + # ========== 统计 ========== + avg_delta_e_all = sum(delta_e_values) / len(delta_e_values) + max_delta_e_all = max(delta_e_values) + min_delta_e_all = min(delta_e_values) + + excellent_count_all = sum(1 for de in delta_e_values if de < 3) + good_count_all = sum(1 for de in delta_e_values if 3 <= de < 5) + poor_count_all = sum(1 for de in delta_e_values if de >= 5) + + delta_e_gray = delta_e_values[0:5] + avg_delta_e_gray = sum(delta_e_gray) / len(delta_e_gray) + + delta_e_colorchecker = delta_e_values[5:23] + avg_delta_e_colorchecker = sum(delta_e_colorchecker) / len(delta_e_colorchecker) + + delta_e_saturated = delta_e_values[23:29] + avg_delta_e_saturated = sum(delta_e_saturated) / len(delta_e_saturated) + + self.log_gui.log("=" * 50) + self.log_gui.log("色准统计(全 29色):") + self.log_gui.log("=" * 50) + self.log_gui.log(f" 平均 ΔE: {avg_delta_e_all:.2f}") + self.log_gui.log(f" 最大 ΔE: {max_delta_e_all:.2f}") + self.log_gui.log(f" 最小 ΔE: {min_delta_e_all:.2f}") + self.log_gui.log(f" 优秀 (ΔE<3): {excellent_count_all} 个") + self.log_gui.log(f" 良好 (3≤ΔE<5): {good_count_all} 个") + self.log_gui.log(f" 偏差 (ΔE≥5): {poor_count_all} 个") + + self.log_gui.log("") + self.log_gui.log("分组统计:") + self.log_gui.log(f" 灰阶 (5个): 平均 ΔE = {avg_delta_e_gray:.2f}") + self.log_gui.log( + f" ColorChecker (18个): 平均 ΔE = {avg_delta_e_colorchecker:.2f}" + ) + self.log_gui.log(f" 饱和色 (6个): 平均 ΔE = {avg_delta_e_saturated:.2f}") + + # ========== 保存测试结果 ========== + accuracy_data = { + "color_patches": color_patches, + "delta_e_values": delta_e_values, + "color_measurements": measured_data_list, + "avg_delta_e": avg_delta_e_all, + "max_delta_e": max_delta_e_all, + "min_delta_e": min_delta_e_all, + "excellent_count": excellent_count_all, + "good_count": good_count_all, + "poor_count": poor_count_all, + "avg_delta_e_gray": avg_delta_e_gray, + "avg_delta_e_colorchecker": avg_delta_e_colorchecker, + "avg_delta_e_saturated": avg_delta_e_saturated, + "target_gamma": target_gamma, + } + + self.results.set_test_item_result("accuracy", accuracy_data) + + # ========== 绘制图表 ========== + self.plot_accuracy(accuracy_data, test_type) + + self.log_gui.log("色准测试完成") + + +def on_test_completed(self): + """测试完成后的UI更新""" + self.testing = False + self.start_btn.config(state=tk.NORMAL) + self.stop_btn.config(state=tk.DISABLED) + self.save_btn.config(state=tk.NORMAL) + self.clear_config_btn.config(state=tk.NORMAL) + self.status_var.set("测试完成") + self.log_gui.log("测试完成") + + # 恢复配置项按钮 + if hasattr(self, "config_panel_frame"): + try: + self.config_panel_frame.btn.configure(state="normal") + except: + pass + + # 启用色域参考标准下拉框 + try: + test_type = self.config.current_test_type + + if test_type == "screen_module" and hasattr(self, "screen_gamut_combo"): + self.screen_gamut_combo.configure(state="readonly") + self.log_gui.log("✓ 屏模组色域参考标准已启用") + + elif test_type == "sdr_movie" and hasattr(self, "sdr_gamut_combo"): + self.sdr_gamut_combo.configure(state="readonly") + self.log_gui.log("✓ SDR 色域参考标准已启用") + + elif test_type == "hdr_movie" and hasattr(self, "hdr_gamut_combo"): + self.hdr_gamut_combo.configure(state="readonly") + self.log_gui.log("✓ HDR 色域参考标准已启用") + except Exception as e: + self.log_gui.log(f"启用色域参考标准失败: {str(e)}") + + # 获取当前测试类型和选中的测试项 + selected_items = self.get_selected_test_items() + test_type = self.config.current_test_type + + # ==================== ✅ 启用单步调试按钮 ==================== + if hasattr(self, "debug_panel"): + try: + # 屏模组:启用 Gamma 和 RGB 单步调试 + if test_type == "screen_module": + if "gamma" in selected_items: + gray_data = self.results.get_intermediate_data("shared", "gray") + if gray_data: + self.debug_panel.enable_debug( + "screen_module", "gamma", gray_data + ) + + # 启用 RGB 单步调试(色域测试完成后) + if "gamut" in selected_items: + rgb_data = self.results.get_intermediate_data("gamut", "rgb") + if rgb_data: + self.debug_panel.enable_debug( + "screen_module", "rgb", rgb_data + ) + + # ✅ 启用单步调试按钮 + if hasattr(self, "screen_debug_btn"): + self.screen_debug_btn.config(state=tk.NORMAL) + self.log_gui.log("✓ 屏模组单步调试按钮已启用") + + # SDR:启用 Gamma、色准和 RGB 单步调试 + elif test_type == "sdr_movie": + if "gamma" in selected_items: + gray_data = self.results.get_intermediate_data("shared", "gray") + if gray_data: + self.debug_panel.enable_debug( + "sdr_movie", "gamma", gray_data + ) + + if "accuracy" in selected_items: + accuracy_data = self.results.get_intermediate_data( + "accuracy", "measured" + ) + if accuracy_data: + self.debug_panel.enable_debug( + "sdr_movie", "accuracy", accuracy_data + ) + + # 启用 RGB 单步调试(色域测试完成后) + if "gamut" in selected_items: + rgb_data = self.results.get_intermediate_data("gamut", "rgb") + if rgb_data: + self.debug_panel.enable_debug("sdr_movie", "rgb", rgb_data) + + # ✅ 启用单步调试按钮 + if hasattr(self, "sdr_debug_btn"): + self.sdr_debug_btn.config(state=tk.NORMAL) + self.log_gui.log("✓ SDR 单步调试按钮已启用") + + # HDR:启用 EOTF、色准和 RGB 单步调试 + elif test_type == "hdr_movie": + if "eotf" in selected_items: + gray_data = self.results.get_intermediate_data("shared", "gray") + if gray_data: + self.debug_panel.enable_debug( + "hdr_movie", "eotf", gray_data + ) + + if "accuracy" in selected_items: + accuracy_data = self.results.get_intermediate_data( + "accuracy", "measured" + ) + if accuracy_data: + self.debug_panel.enable_debug( + "hdr_movie", "accuracy", accuracy_data + ) + + # 启用 RGB 单步调试(色域测试完成后) + if "gamut" in selected_items: + rgb_data = self.results.get_intermediate_data("gamut", "rgb") + if rgb_data: + self.debug_panel.enable_debug("hdr_movie", "rgb", rgb_data) + + # ✅ 启用单步调试按钮 + if hasattr(self, "hdr_debug_btn"): + self.hdr_debug_btn.config(state=tk.NORMAL) + self.log_gui.log("✓ HDR 单步调试按钮已启用") + + except Exception as e: + self.log_gui.log(f"启用单步调试失败: {str(e)}") + + # ==================== 显示色度/色域重新计算按钮 ==================== + if "cct" in selected_items: + try: + if test_type == "screen_module" and hasattr(self, "recalc_cct_btn"): + self.recalc_cct_btn.grid() + self.log_gui.log("✓ 屏模组色度参数调整按钮已启用") + elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_cct_btn"): + self.sdr_recalc_cct_btn.grid() + self.log_gui.log("✓ SDR 色度参数调整按钮已启用") + elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_cct_btn"): + self.hdr_recalc_cct_btn.grid() + self.log_gui.log("✓ HDR 色度参数调整按钮已启用") + except Exception as e: + self.log_gui.log(f"显示色度重新计算按钮失败: {str(e)}") + + if "gamut" in selected_items: + try: + if test_type == "screen_module" and hasattr(self, "recalc_gamut_btn"): + self.recalc_gamut_btn.grid() + self.log_gui.log("✓ 屏模组色域参考调整按钮已启用") + elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_gamut_btn"): + self.sdr_recalc_gamut_btn.grid() + self.log_gui.log("✓ SDR 色域参考调整按钮已启用") + elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_gamut_btn"): + self.hdr_recalc_gamut_btn.grid() + self.log_gui.log("✓ HDR 色域参考调整按钮已启用") + except Exception as e: + self.log_gui.log(f"显示色域重新计算按钮失败: {str(e)}") + + messagebox.showinfo("完成", "测试已完成!") + + +def on_custom_template_test_completed(self): + """客户模板测试完成后的UI更新""" + self.testing = False + self.set_custom_result_table_locked(False) + self.start_btn.config(state=tk.NORMAL) + self.stop_btn.config(state=tk.DISABLED) + self.save_btn.config(state=tk.DISABLED) + self.clear_config_btn.config(state=tk.NORMAL) + self.custom_btn.config(state=tk.NORMAL) + self.status_var.set("客户模板测试完成") + + if hasattr(self, "config_panel_frame"): + try: + self.config_panel_frame.btn.configure(state="normal") + except: + pass + + self.log_gui.log("客户模板测试完成") + messagebox.showinfo("完成", "客户模板测试已完成!") + + +def get_current_test_result(self): + """获取当前测试结果""" + test_type = self.test_type_var.get() + test_items = self.get_selected_test_items() + + # 构建测试结果字典 + result = { + "test_type": test_type, + "test_type_name": self.get_test_type_name(test_type), + "test_items": test_items, + "test_items_names": self.config.get_test_item_chinese_names(test_items), + "timestamp": datetime.datetime.now(), + "status": "完成", + "results": {}, + } + + # 根据测试项目收集结果数据 + for item in test_items: + if item == "gamut" and hasattr(self, "gamut_results"): + result["results"]["gamut"] = getattr(self, "gamut_results", {}) + elif item in ["gamma", "eotf"] and hasattr(self, "gamma_results"): + result["results"][item] = getattr(self, "gamma_results", {}) + elif item == "cct" and hasattr(self, "cct_results"): + result["results"]["cct"] = getattr(self, "cct_results", {}) + elif item == "contrast" and hasattr(self, "contrast_results"): + result["results"]["contrast"] = getattr(self, "contrast_results", {}) + elif item == "accuracy" and hasattr(self, "accuracy_results"): + result["results"]["accuracy"] = getattr(self, "accuracy_results", {}) + + return result + + +def on_test_error(self): + """测试出错后的UI更新""" + self.testing = False + self.set_custom_result_table_locked(False) + self.start_btn.config(state=tk.NORMAL) + self.stop_btn.config(state=tk.DISABLED) + self.clear_config_btn.config(state=tk.NORMAL) + if hasattr(self, "custom_btn"): + self.custom_btn.config(state=tk.NORMAL) + self.status_var.set("测试出错") + + # 恢复配置项按钮 + if hasattr(self, "config_panel_frame"): + try: + self.config_panel_frame.btn.configure(state="normal") + except: + pass + + messagebox.showerror("错误", "测试过程中发生错误,请查看日志") + + diff --git a/app/tests/local_dimming.py b/app/tests/local_dimming.py index eec2318..1d2622c 100644 --- a/app/tests/local_dimming.py +++ b/app/tests/local_dimming.py @@ -8,9 +8,8 @@ import threading import tkinter as tk from tkinter import filedialog, messagebox -def start_local_dimming_test(app): +def start_local_dimming_test(self): """开始 Local Dimming 测试""" - self = app # 检查设备连接 if not self.ca or not self.ucd.status: messagebox.showerror("错误", "请先连接 CA410 和 UCD323") @@ -65,9 +64,8 @@ def start_local_dimming_test(app): threading.Thread(target=run_test, daemon=True).start() -def update_ld_results(app, results): +def update_ld_results(self, results): """更新 Local Dimming 结果显示""" - self = app for percentage, x, y, lv, X, Y, Z in results: self.ld_tree.insert( "", @@ -76,16 +74,14 @@ def update_ld_results(app, results): ) -def stop_local_dimming_test(app): +def stop_local_dimming_test(self): """停止 Local Dimming 测试""" - self = app if hasattr(self, "ld_test_instance"): self.ld_test_instance.stop() -def send_ld_window(app, percentage): +def send_ld_window(self, percentage): """发送指定百分比的窗口""" - self = app if not self.ucd.status: messagebox.showwarning("警告", "请先连接 UCD323 设备") return @@ -120,9 +116,8 @@ def send_ld_window(app, percentage): threading.Thread(target=send, daemon=True).start() -def measure_ld_luminance(app): +def measure_ld_luminance(self): """测量当前亮度""" - self = app if not self.ca: messagebox.showwarning("警告", "请先连接 CA410 色度计") return @@ -178,9 +173,8 @@ def measure_ld_luminance(app): threading.Thread(target=measure, daemon=True).start() -def clear_ld_records(app): +def clear_ld_records(self): """清空测试记录""" - self = app for item in self.ld_tree.get_children(): self.ld_tree.delete(item) self.ld_result_label.config(text="亮度: -- cd/m² | x: -- | y: --") @@ -188,9 +182,8 @@ def clear_ld_records(app): self.log_gui.log("🗑️ 测试记录已清空") -def save_local_dimming_results(app): +def save_local_dimming_results(self): """保存 Local Dimming 结果""" - self = app from tkinter import filedialog import csv import datetime diff --git a/app/views/chart_frame.py b/app/views/chart_frame.py index b3013fd..28904a2 100644 --- a/app/views/chart_frame.py +++ b/app/views/chart_frame.py @@ -10,9 +10,8 @@ import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from views.pq_debug_panel import PQDebugPanel -def init_gamut_chart(app): +def init_gamut_chart(self): """初始化色域图表 - 手动设置subplot位置,完全避免重叠""" - self = app container = ttk.Frame(self.gamut_chart_frame) container.pack(expand=True, fill=tk.BOTH) @@ -47,9 +46,8 @@ def init_gamut_chart(app): self.gamut_canvas.draw() -def init_gamma_chart(app): +def init_gamma_chart(self): """初始化Gamma曲线图表 - 左侧曲线 + 右侧表格(✅ 4列 + 通用说明)""" - self = app container = ttk.Frame(self.gamma_chart_frame) container.pack(expand=True, fill=tk.BOTH) @@ -157,9 +155,8 @@ def init_gamma_chart(app): self.gamma_fig.suptitle("Gamma曲线 + 数据表格", fontsize=12, y=0.98) self.gamma_canvas.draw() -def init_eotf_chart(app): +def init_eotf_chart(self): """初始化 EOTF 曲线图表(HDR 专用)- 左侧曲线 + 右侧表格(✅ 4列)""" - self = app container = ttk.Frame(self.eotf_chart_frame) container.pack(expand=True, fill=tk.BOTH) @@ -263,9 +260,8 @@ def init_eotf_chart(app): self.eotf_fig.suptitle("EOTF 曲线 + 数据表格", fontsize=12, y=0.98) self.eotf_canvas.draw() -def init_cct_chart(app): +def init_cct_chart(self): """初始化色度坐标图表 - 正向横坐标,标题居中最上方""" - self = app container = ttk.Frame(self.cct_chart_frame) container.pack(expand=True) @@ -309,9 +305,8 @@ def init_cct_chart(app): self.cct_canvas.draw() -def init_contrast_chart(app): +def init_contrast_chart(self): """初始化对比度图表 - 固定大小,居中显示""" - self = app container = ttk.Frame(self.contrast_chart_frame) container.pack(expand=True) @@ -345,9 +340,8 @@ def init_contrast_chart(app): self.contrast_canvas.draw() -def init_accuracy_chart(app): +def init_accuracy_chart(self): """初始化色准图表 - 固定大小,居中显示""" - self = app container = ttk.Frame(self.accuracy_chart_frame) container.pack(expand=True) @@ -381,9 +375,8 @@ def init_accuracy_chart(app): self.accuracy_canvas.draw() -def clear_chart(app): +def clear_chart(self): """清空所有图表""" - self = app # ========== 1. 清空色域图表 ========== if hasattr(self, "gamut_ax_xy") and hasattr(self, "gamut_ax_uv"): @@ -678,7 +671,7 @@ def clear_chart(app): self.accuracy_canvas.draw() -def update_chart_tabs_state(app): +def update_chart_tabs_state(self): """根据测试项目复选框状态动态增删图表 Tab(保持规范顺序)。 - 色域 / Gamma 或 EOTF / 色度一致性 / 对比度 / 色准 全部走动态 add/forget @@ -686,7 +679,6 @@ def update_chart_tabs_state(app): - 屏模组测试强制隐藏色准 Tab - 客户模板 Tab 由 change_test_type 独立管理,这里不动 """ - self = app if not hasattr(self, "chart_notebook"): return @@ -751,9 +743,8 @@ def update_chart_tabs_state(app): if hasattr(self, "log_gui"): self.log_gui.log(f"更新Tab状态失败: {str(e)}") -def create_result_chart_frame(app): +def create_result_chart_frame(self): """创建结果图表区域 - 6个独立Tab(Gamma 和 EOTF 分离)""" - self = app # 创建Notebook用于图表切换 self.chart_notebook = ttk.Notebook(self.result_frame) self.chart_notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) @@ -812,9 +803,8 @@ def create_result_chart_frame(app): self.log_gui.log("✓ 单步调试面板已创建(放在测试结果图表下方)") -def on_chart_tab_changed(app, event): +def on_chart_tab_changed(self, event): """Tab切换时的事件处理""" - self = app try: self._last_tab_index = self.chart_notebook.index( self.chart_notebook.select() diff --git a/pqAutomationApp.py b/pqAutomationApp.py index 0af5bb3..d67c68f 100644 --- a/pqAutomationApp.py +++ b/pqAutomationApp.py @@ -90,6 +90,26 @@ from app.device.connection import ( refresh_com_ports as _dev_refresh_com_ports, update_connection_indicator as _dev_update_connection_indicator, ) +from app.runner.test_runner import ( + get_current_test_result as _run_get_current_test_result, + new_pq_results as _run_new_pq_results, + on_custom_template_test_completed as _run_on_custom_template_test_completed, + on_test_completed as _run_on_test_completed, + on_test_error as _run_on_test_error, + run_custom_sdr_test as _run_run_custom_sdr_test, + run_hdr_movie_test as _run_run_hdr_movie_test, + run_screen_module_test as _run_run_screen_module_test, + run_sdr_movie_test as _run_run_sdr_movie_test, + run_test as _run_run_test, + send_fix_pattern as _run_send_fix_pattern, + test_cct as _run_test_cct, + test_color_accuracy as _run_test_color_accuracy, + test_contrast as _run_test_contrast, + test_custom_sdr as _run_test_custom_sdr, + test_eotf as _run_test_eotf, + test_gamma as _run_test_gamma, + test_gamut as _run_test_gamut, +) plt.rcParams["font.family"] = ["sans-serif"] plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] @@ -211,9 +231,7 @@ class PQAutomationApp: ) self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) - def get_config_path(self): - """转发到 app.config_io.get_config_path(Step 4 重构)""" - return _cfg_get_config_path(self) + get_config_path = _cfg_get_config_path def initialize_default_test_type(self): """初始化默认测试类型(在所有控件创建完成后调用)""" try: @@ -226,27 +244,13 @@ class PQAutomationApp: if hasattr(self, "log_gui"): self.log_gui.log(f"初始化默认测试类型失败: {str(e)}") - def init_gamut_chart(self): - """转发到 app.views.chart_frame.init_gamut_chart(Step 3 重构)""" - return _cf_init_gamut_chart(self) - def init_gamma_chart(self): - """转发到 app.views.chart_frame.init_gamma_chart(Step 3 重构)""" - return _cf_init_gamma_chart(self) - def init_eotf_chart(self): - """转发到 app.views.chart_frame.init_eotf_chart(Step 3 重构)""" - return _cf_init_eotf_chart(self) - def init_cct_chart(self): - """转发到 app.views.chart_frame.init_cct_chart(Step 3 重构)""" - return _cf_init_cct_chart(self) - def init_contrast_chart(self): - """转发到 app.views.chart_frame.init_contrast_chart(Step 3 重构)""" - return _cf_init_contrast_chart(self) - def init_accuracy_chart(self): - """转发到 app.views.chart_frame.init_accuracy_chart(Step 3 重构)""" - return _cf_init_accuracy_chart(self) - def clear_chart(self): - """转发到 app.views.chart_frame.clear_chart(Step 3 重构)""" - return _cf_clear_chart(self) + init_gamut_chart = _cf_init_gamut_chart + init_gamma_chart = _cf_init_gamma_chart + init_eotf_chart = _cf_init_eotf_chart + init_cct_chart = _cf_init_cct_chart + init_contrast_chart = _cf_init_contrast_chart + init_accuracy_chart = _cf_init_accuracy_chart + clear_chart = _cf_clear_chart def create_floating_config_panel(self): """创建右上角悬浮配置框""" cf = CollapsingFrame(self.control_frame_top) @@ -1230,9 +1234,7 @@ class PQAutomationApp: self.update_config() self.update_chart_tabs_state() - def update_chart_tabs_state(self): - """转发到 app.views.chart_frame.update_chart_tabs_state(Step 3 重构)""" - return _cf_update_chart_tabs_state(self) + update_chart_tabs_state = _cf_update_chart_tabs_state def get_test_type_display_name(self, test_type): """获取测试类型的显示名称""" display_names = { @@ -1518,22 +1520,14 @@ class PQAutomationApp: ca_channel_combo.grid(row=2, column=1, sticky=ttk.W, padx=5, pady=3) ca_channel_combo.bind("<>", self.update_config) - def get_available_ucd_ports(self): - return _dev_get_available_ucd_ports(self) - def get_available_com_ports(self): - return _dev_get_available_com_ports(self) - def refresh_com_ports(self): - return _dev_refresh_com_ports(self) - def check_com_connections(self): - return _dev_check_com_connections(self) - def update_connection_indicator(self, indicator, connected): - return _dev_update_connection_indicator(self, indicator, connected) - def check_port_connection(self, is_ucd=True): - return _dev_check_port_connection(self, is_ucd) - def enable_com_widgets(self): - return _dev_enable_com_widgets(self) - def disconnect_com_connections(self): - return _dev_disconnect_com_connections(self) + get_available_ucd_ports = _dev_get_available_ucd_ports + get_available_com_ports = _dev_get_available_com_ports + refresh_com_ports = _dev_refresh_com_ports + check_com_connections = _dev_check_com_connections + update_connection_indicator = _dev_update_connection_indicator + check_port_connection = _dev_check_port_connection + enable_com_widgets = _dev_enable_com_widgets + disconnect_com_connections = _dev_disconnect_com_connections def create_test_type_frame(self): """创建测试类型选择区域(侧边栏形式)""" # 设置测试类型变量 @@ -2471,12 +2465,8 @@ class PQAutomationApp: """切换日志面板的显示状态""" self.show_panel("log") - def create_result_chart_frame(self): - """转发到 app.views.chart_frame.create_result_chart_frame(Step 3 重构)""" - return _cf_create_result_chart_frame(self) - def on_chart_tab_changed(self, event): - """转发到 app.views.chart_frame.on_chart_tab_changed(Step 3 重构)""" - return _cf_on_chart_tab_changed(self, event) + create_result_chart_frame = _cf_create_result_chart_frame + on_chart_tab_changed = _cf_on_chart_tab_changed def change_test_type(self, test_type): """切换测试类型""" # 切换测试类型时,自动隐藏日志面板和 Local Dimming 面板 @@ -4733,1485 +4723,42 @@ class PQAutomationApp: self.log_gui.log(traceback.format_exc()) messagebox.showerror("错误", f"保存测试结果失败: {str(e)}") - def new_pq_results(self, test_type, test_name): - self.results = PQResult(test_type, test_name) - # 设置配置 - config = { - "test_type": test_type, - "test_name": test_name, - "test_items": self.config.current_test_types[test_type]["test_items"], - "test_items_chinese": self.config.get_test_item_chinese_names( - self.config.current_test_types[test_type]["test_items"] - ), - } - self.results.set_test_config(config) - - # 添加测试项 - for item in config["test_items"]: - self.results.add_test_item( - item, config["test_items_chinese"][config["test_items"].index(item)] - ) - - def run_test(self, test_type, test_items): - """执行测试""" - try: - self.log_gui.log(f"开始执行{self.get_test_type_name(test_type)}测试") - self.log_gui.log( - f"测试项目: {', '.join(self.config.get_test_item_chinese_names(test_items))}" - ) - - # 根据测试类型执行不同的测试流程 - if test_type == "screen_module": - self.run_screen_module_test(test_items) - elif test_type == "sdr_movie": - self.run_sdr_movie_test(test_items) - elif test_type == "hdr_movie": - self.run_hdr_movie_test(test_items) - - # 测试完成后更新UI状态 - if self.testing: # 如果没有被中途停止 - self.root.after(0, self.on_test_completed) - except Exception as e: - self.log_gui.log(f"测试过程中发生错误: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - self.root.after(0, self.on_test_error) - - def run_screen_module_test(self, test_items): - """执行屏模组性能测试 - 优化版""" - self.log_gui.log("执行屏模组性能测试...") - - if test_items: - self.new_pq_results("screen_module", "屏模组性能测试") - else: - self.log_gui.log("未选择任何测试项目") - return - - # 判断是否需要灰阶数据 - needs_gray_data = any( - item in test_items for item in ["gamma", "cct", "contrast"] - ) - shared_gray_data = None # 共享的灰阶数据 - - # 计算总测试项数量 - total_items = len(test_items) - current_item = 0 - - for item in test_items: - if not self.testing: # 检查是否被停止 - return - - current_item += 1 - self.status_var.set(f"测试进行中... ({current_item}/{total_items})") - - # ==================== 色域测试 ==================== - if item == "gamut": - self.test_gamut("screen_module") - - # ==================== 灰阶数据采集 ==================== - # 如果是第一个需要灰阶数据的测试项,统一采集数据 - elif ( - item in ["gamma", "cct", "contrast"] - and shared_gray_data is None - and needs_gray_data - ): - self.log_gui.log("=" * 50) - self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") - self.log_gui.log("=" * 50) - - shared_gray_data = self.send_fix_pattern("gray") - - if not shared_gray_data or len(shared_gray_data) < 2: - self.log_gui.log("灰阶数据采集失败或数据不足,跳过相关测试") - return - - self.log_gui.log( - f"✓ 灰阶数据采集完成,共 {len(shared_gray_data)} 个数据点" - ) - - # 保存到 results 对象,供所有灰阶测试使用 - self.results.add_intermediate_data("shared", "gray", shared_gray_data) - - # 执行当前测试项 - if item == "gamma": - self.test_gamma("screen_module", shared_gray_data) - elif item == "cct": - self.test_cct("screen_module", shared_gray_data) - elif item == "contrast": - self.test_contrast("screen_module", shared_gray_data) - - # ==================== 后续灰阶测试(复用数据) ==================== - elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: - self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") - - if item == "gamma": - self.test_gamma("screen_module", shared_gray_data) - elif item == "cct": - self.test_cct("screen_module", shared_gray_data) - elif item == "contrast": - self.test_contrast("screen_module", shared_gray_data) - - - - def run_custom_sdr_test(self, test_items): - """执行客户定制 SDR 测试 - 升级版""" - self.log_gui.log("执行客户定制 SDR 测试...") - # 获取信号格式设置 - color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 - gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6 - data_range = self.sdr_data_range_var.get() # Full/Limited - bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit - - self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}") - self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") - self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") - self.test_custom_sdr() - - if self.testing: - self.root.after(0, self.on_custom_template_test_completed) - - - - - def run_sdr_movie_test(self, test_items): - """执行SDR Movie测试""" - self.log_gui.log("执行SDR Movie测试...") - - if test_items: - self.new_pq_results("sdr_movie", "SDR Movie测试") - else: - self.log_gui.log("未选择任何测试项目") - return - - # 获取信号格式设置 - color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 - gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6 - data_range = self.sdr_data_range_var.get() # Full/Limited - bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit - - self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}") - self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") - - # 判断是否需要灰阶数据 - needs_gray_data = any( - item in test_items for item in ["gamma", "cct", "contrast"] - ) - shared_gray_data = None - - # 计算总测试项数量 - total_items = len(test_items) - current_item = 0 - - for item in test_items: - if not self.testing: - return - - current_item += 1 - self.status_var.set(f"测试进行中... ({current_item}/{total_items})") - - if item == "gamut": - self.test_gamut("sdr_movie") - - elif ( - item in ["gamma", "cct", "contrast"] - and shared_gray_data is None - and needs_gray_data - ): - self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") - shared_gray_data = self.send_fix_pattern("gray") - - if not shared_gray_data or len(shared_gray_data) < 2: - self.log_gui.log("灰阶数据采集失败或数据不足") - return - - self.results.add_intermediate_data("shared", "gray", shared_gray_data) - - if item == "gamma": - self.test_gamma("sdr_movie", shared_gray_data) - elif item == "cct": - self.test_cct("sdr_movie", shared_gray_data) - elif item == "contrast": - self.test_contrast("sdr_movie", shared_gray_data) - - elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: - self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") - if item == "gamma": - self.test_gamma("sdr_movie", shared_gray_data) - elif item == "cct": - self.test_cct("sdr_movie", shared_gray_data) - elif item == "contrast": - self.test_contrast("sdr_movie", shared_gray_data) - - elif item == "accuracy": - self.test_color_accuracy("sdr_movie") - - def run_hdr_movie_test(self, test_items): - """执行HDR Movie测试""" - self.log_gui.log("执行HDR Movie测试...") - - if test_items: - self.new_pq_results("hdr_movie", "HDR Movie测试") - else: - self.log_gui.log("未选择任何测试项目") - return - - # 获取信号格式设置 - color_space = self.hdr_color_space_var.get() - max_cll = self.hdr_maxcll_var.get() - max_fall = self.hdr_maxfall_var.get() - data_range = self.hdr_data_range_var.get() - bit_depth = self.hdr_bit_depth_var.get() - - self.log_gui.log(f"信号格式: 色彩空间={color_space}") - self.log_gui.log(f" MaxCLL={max_cll}, MaxFALL={max_fall}") - self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") - - # 判断是否需要灰阶数据 - needs_gray_data = any( - item in test_items for item in ["eotf", "cct", "contrast"] - ) - shared_gray_data = None - - # 计算总测试项数量 - total_items = len(test_items) - current_item = 0 - - for item in test_items: - if not self.testing: - return - - current_item += 1 - self.status_var.set(f"测试进行中... ({current_item}/{total_items})") - - if item == "gamut": - self.test_gamut("hdr_movie") - - elif ( - item in ["eotf", "cct", "contrast"] - and shared_gray_data is None - and needs_gray_data - ): - self.log_gui.log("开始统一采集灰阶数据(用于 EOTF/CCT/对比度测试)") - shared_gray_data = self.send_fix_pattern("gray") - - if not shared_gray_data or len(shared_gray_data) < 2: - self.log_gui.log("灰阶数据采集失败或数据不足") - return - - self.results.add_intermediate_data("shared", "gray", shared_gray_data) - - if item == "eotf": - self.test_eotf("hdr_movie", shared_gray_data) - elif item == "cct": - self.test_cct("hdr_movie", shared_gray_data) - elif item == "contrast": - self.test_contrast("hdr_movie", shared_gray_data) - - elif item in ["eotf", "cct", "contrast"] and shared_gray_data is not None: - self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") - if item == "eotf": - self.test_eotf("hdr_movie", shared_gray_data) - elif item == "cct": - self.test_cct("hdr_movie", shared_gray_data) - elif item == "contrast": - self.test_contrast("hdr_movie", shared_gray_data) - - elif item == "accuracy": - self.test_color_accuracy("hdr_movie") - - def send_fix_pattern(self, mode): - """发送固定图案并采集数据 - 支持不同测试类型的信号格式""" - results = [] - - try: - # 1. 设置图案模式 - if mode == "rgb": - self.config.set_current_pattern("rgb") - elif mode == "gray": - self.config.set_current_pattern("gray") - elif mode == "accuracy": # 色准模式(SDR 和 HDR 通用 29色) - self.config.set_current_pattern("accuracy") - elif mode == "custom": - self.config.set_current_pattern("custom") - else: - self.log_gui.log(f"❌ 未知的图案模式: {mode}") - return None - - # 2. 获取当前测试类型 - test_type = self.config.current_test_type - - # 3. 根据测试类型设置信号格式和图案 - if test_type == "screen_module": - # 屏模组测试:使用 Timing - self.log_gui.log("=" * 50) - self.log_gui.log("设置屏模组信号格式:") - self.log_gui.log("=" * 50) - - timing_str = self.config.current_test_types[test_type]["timing"] - self.log_gui.log(f" Timing: {timing_str}") - - # ✅ 屏模组测试:直接使用原始配置 - self.ucd.set_ucd_params(self.config) - - elif test_type == "sdr_movie": - # SDR 测试:设置色彩空间、Gamma 等 - self.log_gui.log("=" * 50) - self.log_gui.log("设置 SDR 信号格式:") - self.log_gui.log("=" * 50) - - color_space = self.sdr_color_space_var.get() - gamma = self.sdr_gamma_type_var.get() - data_range = self.sdr_data_range_var.get() - bit_depth = self.sdr_bit_depth_var.get() - - self.log_gui.log(f" 色彩空间: {color_space}") - self.log_gui.log(f" Gamma: {gamma}") - self.log_gui.log(f" 数据范围: {data_range}") - self.log_gui.log(f" 编码位深: {bit_depth}") - - success = self.ucd.set_sdr_format( - color_space=color_space, - gamma=gamma, - data_range=data_range, - bit_depth=bit_depth, - ) - - if success: - self.log_gui.log("✓ SDR 信号格式设置成功") - else: - self.log_gui.log("✗ SDR 信号格式设置失败") - - # 设置图案参数 - if mode == "accuracy": - self.log_gui.log(f"设置 SDR 29色色准测试图案...") - else: - self.log_gui.log(f"设置 SDR 测试图案({mode} 模式)...") - - # ========== ✅✅✅ 修改:使用临时配置对象 ========== - import copy - - # 从原始配置获取参数(每次都是干净的) - if mode == "rgb": - original_params = copy.deepcopy( - self.config.default_pattern_rgb["pattern_params"] - ) - elif mode == "gray": - original_params = copy.deepcopy( - self.config.default_pattern_gray["pattern_params"] - ) - elif mode == "accuracy": - original_params = copy.deepcopy( - self.config.default_pattern_accuracy["pattern_params"] - ) - elif mode == "custom": - original_params = copy.deepcopy( - self.config.default_pattern_temp["pattern_params"] - ) - - self.log_gui.log(f"🔍 使用原始 RGB 参数(前 3 个):") - for i in range(min(3, len(original_params))): - self.log_gui.log(f" [{i+1}] {original_params[i]}") - - # 根据 data_range 转换 - converted_params = convert_pattern_params( - pattern_params=original_params, data_range=data_range, verbose=False - ) - - if data_range == "Limited": - self.log_gui.log("🔧 转换为 Limited Range (16-235):") - for i in range(min(3, len(converted_params))): - self.log_gui.log( - f" {original_params[i]} → {converted_params[i]}" - ) - else: - self.log_gui.log("✓ Full Range,RGB 保持不变") - - # ✅ 创建临时配置对象(不修改 self.config) - temp_config = self.config.get_temp_config_with_converted_params( - mode=mode, converted_params=converted_params - ) - - # ✅ 使用临时配置设置参数 - self.ucd.set_ucd_params(temp_config) - - self.log_gui.log(f"✓ 图案参数已设置,共 {len(converted_params)} 个图案") - # ========== 修改结束 ========== - - elif test_type == "hdr_movie": - # HDR 测试:设置色彩空间、Metadata 等 - self.log_gui.log("=" * 50) - self.log_gui.log("设置 HDR 信号格式:") - self.log_gui.log("=" * 50) - - color_space = self.hdr_color_space_var.get() - data_range = self.hdr_data_range_var.get() - bit_depth = self.hdr_bit_depth_var.get() - max_cll = self.hdr_maxcll_var.get() - max_fall = self.hdr_maxfall_var.get() - - self.log_gui.log(f" 色彩空间: {color_space}") - self.log_gui.log(f" 数据范围: {data_range}") - self.log_gui.log(f" 编码位深: {bit_depth}") - self.log_gui.log(f" MaxCLL: {max_cll}") - self.log_gui.log(f" MaxFALL: {max_fall}") - - success = self.ucd.set_hdr_format( - color_space=color_space, - data_range=data_range, - bit_depth=bit_depth, - max_cll=max_cll, - max_fall=max_fall, - ) - - if success: - self.log_gui.log("✓ HDR 信号格式设置成功") - else: - self.log_gui.log("✗ HDR 信号格式设置失败") - - # 设置图案参数 - if mode == "accuracy": - self.log_gui.log(f"设置 HDR 29色色准测试图案...") - else: - self.log_gui.log(f"设置 HDR 测试图案({mode} 模式)...") - - # ========== ✅✅✅ 修改:使用临时配置对象 ========== - import copy - - # 从原始配置获取参数 - if mode == "rgb": - original_params = copy.deepcopy( - self.config.default_pattern_rgb["pattern_params"] - ) - elif mode == "gray": - original_params = copy.deepcopy( - self.config.default_pattern_gray["pattern_params"] - ) - elif mode == "accuracy": - original_params = copy.deepcopy( - self.config.default_pattern_accuracy["pattern_params"] - ) - - self.log_gui.log(f"🔍 使用原始 RGB 参数(前 3 个):") - for i in range(min(3, len(original_params))): - self.log_gui.log(f" [{i+1}] {original_params[i]}") - - # 根据 data_range 转换 - converted_params = convert_pattern_params( - pattern_params=original_params, data_range=data_range, verbose=False - ) - - if data_range == "Limited": - self.log_gui.log("🔧 转换为 Limited Range (16-235):") - for i in range(min(3, len(converted_params))): - self.log_gui.log( - f" {original_params[i]} → {converted_params[i]}" - ) - else: - self.log_gui.log("✓ Full Range,RGB 保持不变") - - # ✅ 创建临时配置对象 - temp_config = self.config.get_temp_config_with_converted_params( - mode=mode, converted_params=converted_params - ) - - self.ucd.set_ucd_params(temp_config) - - self.log_gui.log(f"✓ 图案参数已设置,共 {len(converted_params)} 个图案") - # ========== 修改结束 ========== - - self.log_gui.log("=" * 50) - - # 4. 循环发送图案并采集数据(使用原始配置的数量) - total_patterns = len(self.config.current_pattern["pattern_params"]) - self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案") - settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0))) - progress_step = max( - 1, int(getattr(self, "pattern_progress_log_step", 5)) - ) - self.log_gui.log( - f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)" - ) - - # 获取颜色名称列表(用于日志显示) - color_names = None - if mode == "accuracy": - color_names = self.config.get_accuracy_color_names() - - custom_pattern_names = [] - if mode == "custom" and hasattr(self.config, "get_temp_pattern_names"): - custom_pattern_names = self.config.get_temp_pattern_names() - - for i in range(total_patterns): - if not self.testing: - self.log_gui.log("⚠️ 测试已停止") - return results - - should_log_detail = ( - i == 0 - or (i + 1) == total_patterns - or ((i + 1) % progress_step == 0) - ) - - # 设置下一个图案(显示颜色名称) - if should_log_detail: - if color_names and i < len(color_names): - self.log_gui.log( - f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..." - ) - else: - self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...") - - self.ucd.set_next_pattern() - self.ucd.run() - time.sleep(settle_time) - - # 测量数据 - if mode == "custom": - result = [] - self.ca.set_Display(1) - tcp, duv, lv, X, Y, Z = self.ca.readAllDisplay() - - if should_log_detail: - self.log_gui.log( - f" ✓ 测量完成: TCP={tcp:.4f}, DUV={duv:.4f}, lv={lv:.2f}, " - f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) - - self.ca.set_Display(8) - lambda_, Pe, lv, X, Y, Z = self.ca.readAllDisplay() - - if should_log_detail: - self.log_gui.log( - f" ✓ 测量完成: λ={lambda_:.4f}, Pe={Pe:.4f}, lv={lv:.2f}, " - f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) - result = [tcp, duv, lv, lambda_, Pe, lv, X, Y, Z] - results.append(result) - - # 每完成一个 pattern,实时写入客户模板结果表。 - try: - xy = colour.XYZ_to_xy(np.array([X, Y, Z])) - u_prime, v_prime, _ = colour.XYZ_to_CIE1976UCS( - np.array([X, Y, Z]) - ) - row_data = { - "pattern_name": ( - custom_pattern_names[i] - if i < len(custom_pattern_names) - else f"P {i + 1}" - ), - "X": X, - "Y": Y, - "Z": Z, - "x": xy[0], - "y": xy[1], - "Lv": lv, - "u_prime": u_prime, - "v_prime": v_prime, - "Tcp": tcp, - "duv": duv, - "lambda_d": lambda_, - "Pe": Pe, - } - self.root.after( - 0, - lambda row_no=i + 1, data=row_data: self.append_custom_template_result( - row_no, data - ), - ) - except Exception as e: - self.log_gui.log(f"⚠️ 第 {i+1} 行实时结果写入失败: {str(e)}") - - else: - self.ca.set_xyLv_Display() - - x, y, lv, X, Y, Z = self.ca.readAllDisplay() - results.append([x, y, lv, X, Y, Z]) - - if should_log_detail: - self.log_gui.log( - f" ✓ 测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " - f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) - - self.log_gui.log(f"✓ 数据采集完成,共 {len(results)} 组数据") - return results - - except Exception as e: - self.log_gui.log(f"❌ 发送图案失败: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - return None - - def test_custom_sdr(self): - """执行客户定制 SDR 测试 - 升级版""" - self.log_gui.log("执行客户定制 SDR 测试...") - results = self.send_fix_pattern("custom") - if not results: - self.log_gui.log("客户模板SDR测试被中断") - return - - self.log_gui.log(f"客户模板采集完成,共 {len(results)} 组数据") - - - - - def test_gamut(self, test_type): - """测试色域""" - self.log_gui.log("开始测试色域...") - self.results.start_test_item("gamut") - - try: - # 存储测量结果 - results = self.send_fix_pattern("rgb") - - # 检查结果是否为空 - if not results: - self.log_gui.log("色域测试被中断") - return - - self.results.add_intermediate_data("gamut", "rgb", results) - - # 计算色域覆盖率 - self.log_gui.log("计算色域覆盖率...") - - # 提取 x, y 坐标用于计算色域 - xy_points = [[result[0], result[1]] for result in results] - - # ========== ✅ 测试时:使用色彩空间的值作为参考标准 ========== - reference_standard = None - area = None - coverage = None - - if test_type == "screen_module": - # 屏模组测试:固定使用 DCI-P3(因为没有色彩空间设置) - reference_standard = "DCI-P3" - - # ✅ 同步更新到色域参考标准变量(供后续重绘使用) - self.screen_gamut_ref_var.set(reference_standard) - - elif test_type == "sdr_movie": - # SDR 测试:使用色彩空间设置 - color_space = self.sdr_color_space_var.get() - - if color_space == "BT.709": - reference_standard = "BT.709" - elif color_space == "BT.601": - reference_standard = "BT.601" - elif color_space == "BT.2020": - reference_standard = "BT.2020" - else: - reference_standard = "BT.709" - self.log_gui.log( - f"⚠️ 未识别的色彩空间 '{color_space}',使用默认标准 BT.709" - ) - - # ✅ 同步更新到色域参考标准变量 - self.sdr_gamut_ref_var.set(reference_standard) - - elif test_type == "hdr_movie": - # HDR 测试:使用色彩空间设置 - color_space = self.hdr_color_space_var.get() - - if color_space == "BT.2020": - reference_standard = "BT.2020" - elif color_space == "DCI-P3": - reference_standard = "DCI-P3" - else: - reference_standard = "BT.2020" - self.log_gui.log( - f"⚠️ 未识别的色彩空间 '{color_space}',使用默认标准 BT.2020" - ) - - # ✅ 同步更新到色域参考标准变量 - self.hdr_gamut_ref_var.set(reference_standard) - else: - # 未知测试类型,使用 DCI-P3 作为后备 - reference_standard = "DCI-P3" - self.log_gui.log( - f"⚠️ 未识别的测试类型 '{test_type}',使用默认标准 DCI-P3" - ) - - # ========== 根据参考标准计算 XY 覆盖率 ========== - if reference_standard == "BT.2020": - area, coverage = pq_algorithm.calculate_gamut_coverage_BT2020(xy_points) - elif reference_standard == "BT.709": - area, coverage = pq_algorithm.calculate_gamut_coverage_BT709(xy_points) - elif reference_standard == "DCI-P3": - area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) - elif reference_standard == "BT.601": - area, coverage = pq_algorithm.calculate_gamut_coverage_BT601(xy_points) - else: - # 默认使用 DCI-P3 - area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) - reference_standard = "DCI-P3" - self.log_gui.log( - f"⚠️ 未识别的参考标准 '{reference_standard}',使用默认标准 DCI-P3" - ) - - # ========== ✅✅✅ 新增:计算 UV 覆盖率 ========== - uv_coverage = 0 - try: - # 将 XY 转换为 UV - uv_points = [] - for x, y in xy_points: - u, v = pq_algorithm.xy_to_uv_1976(x, y) - uv_points.append([u, v]) - - # 根据参考标准计算 UV 覆盖率 - if len(uv_points) >= 3: - if reference_standard == "BT.2020": - _, uv_coverage = ( - pq_algorithm.calculate_gamut_coverage_BT2020_uv(uv_points) - ) - elif reference_standard == "BT.709": - _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT709_uv( - uv_points - ) - elif reference_standard == "DCI-P3": - _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv( - uv_points - ) - elif reference_standard == "BT.601": - _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT601_uv( - uv_points - ) - else: - _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv( - uv_points - ) - - self.log_gui.log( - f"✓ XY 覆盖率: {coverage:.1f}% | UV 覆盖率: {uv_coverage:.1f}%" - ) - except: - uv_coverage = 0 - - # ========== 保存结果时包含 XY 和 UV 覆盖率 ========== - self.results.set_test_item_result( - "gamut", - { - "area": area, - "coverage": coverage, - "uv_coverage": uv_coverage, # ✅ 新增 UV 覆盖率 - "reference": reference_standard, - }, - ) - - # 传递完整的 results 用于绘图 - self.plot_gamut(results, coverage, test_type) - - self.log_gui.log("色域测试完成") - - except Exception as e: - self.log_gui.log(f"色域测试失败: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - raise - - def test_gamma(self, test_type, gray_data=None): - """测试Gamma曲线 - - Args: - test_type: 测阶数据,如果提供则使用,否则重新采集 - """ - self.log_gui.log("开始测试Gamma曲线...") - self.results.start_test_item("gamma") - - try: - # 使用传入的灰阶数据或独立采集 - if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") - results = gray_data - else: - self.log_gui.log("独立采集灰阶数据") - results = self.send_fix_pattern("gray") - - if not results or len(results) < 2: - self.log_gui.log("Gamma测试被中断或数据不足") - return - - self.results.add_intermediate_data("gamma", "gray", results) - - self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算") - self.log_gui.log("计算Gamma值...") - - # ========== ✅ 修复:正确获取 max_index_fix ========== - # 获取配置中的值 - config_max_value = self.config.current_pattern.get( - "measurement_max_value", 10 - ) - - # 强制转换为整数 - try: - max_index_fix = int(config_max_value) - except (ValueError, TypeError): - self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10") - max_index_fix = 10 - - self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}") - - # 关键修复:验证并调整 max_index_fix - # max_index_fix 应该是数据点的最大索引(从0开始,所以是 len - 1) - actual_max_index = len(results) - 1 - - if max_index_fix > actual_max_index: - self.log_gui.log( - f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})" - ) - self.log_gui.log(f"自动调整为: {actual_max_index}") - max_index_fix = actual_max_index - - self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}") - # ======================================================== - - # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) - pattern_params = self.config.default_pattern_gray.get( - "pattern_params", None - ) - - # 计算Gamma值(使用修正后的 max_index_fix 和 8bit pattern参数) - results_with_gamma_list, L_bar = self.calculate_gamma( - results, max_index_fix, pattern_params - ) - self.results.set_test_item_result( - "gamma", {"gamma": results_with_gamma_list, "L_bar": L_bar} - ) - - # 绘制Gamma曲线 - if test_type == "sdr_movie": - try: - target_gamma = float(self.sdr_gamma_type_var.get()) - except (ValueError, AttributeError): - target_gamma = 2.2 - else: - target_gamma = 2.2 - - self.plot_gamma(L_bar, results_with_gamma_list, target_gamma, test_type) - - self.log_gui.log("Gamma测试完成") - - except Exception as e: - self.log_gui.log(f"Gamma测试失败: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - raise - - def test_eotf(self, test_type, gray_data=None): - """测试 EOTF 曲线(HDR 专用) - - Args: - test_type: 测试类型阶数据,如果提供则使用,否则重新采集 - """ - self.log_gui.log("开始测试 EOTF 曲线(HDR)...") - self.results.start_test_item("eotf") - - try: - # 使用传入的灰阶数据或独立采集 - if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") - results = gray_data - else: - self.log_gui.log("独立采集灰阶数据") - results = self.send_fix_pattern("gray") - - if not results or len(results) < 2: - self.log_gui.log("EOTF 测试被中断或数据不足") - return - - self.results.add_intermediate_data("eotf", "gray", results) - - self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算") - self.log_gui.log("计算 EOTF 值...") - - # ========== 获取 max_index_fix ========== - config_max_value = self.config.current_pattern.get( - "measurement_max_value", 10 - ) - - try: - max_index_fix = int(config_max_value) - except (ValueError, TypeError): - self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10") - max_index_fix = 10 - - self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}") - - # 验证并调整 max_index_fix - actual_max_index = len(results) - 1 - - if max_index_fix > actual_max_index: - self.log_gui.log( - f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})" - ) - self.log_gui.log(f"自动调整为: {actual_max_index}") - max_index_fix = actual_max_index - - self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}") - - # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) - pattern_params = self.config.default_pattern_gray.get( - "pattern_params", None - ) - - # ========== 计算 EOTF(复用 Gamma 计算逻辑,使用8bit pattern参数)========== - results_with_eotf_list, L_bar = self.calculate_gamma( - results, max_index_fix, pattern_params - ) - - # 保存结果 - self.results.set_test_item_result( - "eotf", {"eotf": results_with_eotf_list, "L_bar": L_bar} - ) - - # ========== 绘制 EOTF 曲线 ========== - # HDR 使用 PQ 曲线,目标 gamma 设为 None(不使用传统 gamma) - self.plot_eotf(L_bar, results_with_eotf_list, test_type) - - self.log_gui.log("EOTF 测试完成") - - except Exception as e: - self.log_gui.log(f"EOTF 测试失败: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - raise - - def test_cct(self, test_type, gray_data=None): - """测试色度一致性""" - self.log_gui.log("开始测试色度一致性...") - self.results.start_test_item("cct") - - try: - if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") - results = gray_data - else: - self.log_gui.log("独立采集灰阶数据") - results = self.send_fix_pattern("gray") - - if not results: - self.log_gui.log("色度一致性测试被中断") - return - - self.results.add_intermediate_data("cct", "gray", results) - - self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算") - - # 提取色度坐标 - cct_values = pq_algorithm.calculate_cct_from_results(results) - - # 保存到结果 - self.results.set_test_item_result("cct", {"cct_values": cct_values}) - - # 绘制图表 - self.plot_cct(test_type) - - self.log_gui.log("色度一致性测试完成") - except Exception as e: - self.log_gui.log(f"色度一致性测试失败: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - raise - - def test_contrast(self, test_type, gray_data=None): - """测试对比度 - - Args: - test_type: 阶数据,如果提供则使用,否则重新采集 - """ - self.log_gui.log("开始测试对比度...") - self.results.start_test_item("contrast") - - try: - # ✅ 优先使用传入的灰阶数据 - if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") - results = gray_data - else: - self.log_gui.log("独立采集灰阶数据") - results = self.send_fix_pattern("gray") - - if not results: - self.log_gui.log("对比度测试被中断") - return - - self.results.add_intermediate_data("contrast", "gray", results) - - # 获取最亮和最暗的亮度值 - luminance_values = [result[2] for result in results] # 提取lv值 - - max_luminance = max(luminance_values) # 最大亮度(白) - min_luminance = min(luminance_values) # 最小亮度(黑) - - # 防止除以0 - if min_luminance < 0.001: - min_luminance = 0.001 - - # 计算对比度 - contrast_ratio = max_luminance / min_luminance - - # 保存结果 - contrast_data = { - "max_luminance": max_luminance, - "min_luminance": min_luminance, - "contrast_ratio": contrast_ratio, - "luminance_values": luminance_values, - } - self.results.set_test_item_result("contrast", contrast_data) - - # 显示对比度结果到日志 - self.log_gui.log(f"最大亮度 (白场): {max_luminance:.2f} cd/m²") - self.log_gui.log(f"最小亮度 (黑场): {min_luminance:.4f} cd/m²") - self.log_gui.log(f"对比度: {contrast_ratio:.0f}:1") - - # 绘制对比度图表 - self.plot_contrast(contrast_data, test_type) - - self.log_gui.log("对比度测试完成") - except Exception as e: - self.log_gui.log(f"对比度测试失败: {str(e)}") - import traceback - - self.log_gui.log(traceback.format_exc()) - raise - - def calculate_delta_e_2000( - self, measured_x, measured_y, measured_lv, standard_x, standard_y - ): - """转发到 app.tests.color_accuracy.calculate_delta_e_2000(Step 1 重构)""" - return _calc_delta_e_2000( - measured_x, measured_y, measured_lv, standard_x, standard_y - ) - - def test_color_accuracy(self, test_type): - """测试色准 - 使用手工实现的 ΔE 2000(应用 Gamma)""" - - # ========== 读取用户选择的 Gamma ========== - if test_type == "sdr_movie": - try: - target_gamma = float(self.sdr_gamma_type_var.get()) - except (ValueError, AttributeError): - target_gamma = 2.2 - - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始测试色准(SDR Movie 标准 - 29色)") - self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}") # ← 新增 - self.log_gui.log("=" * 50) - - elif test_type == "hdr_movie": - target_gamma = 2.4 # HDR 使用 PQ,但保留参考值 - - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始测试色准(HDR Movie 标准 - 29色)") - self.log_gui.log(f"✓ 使用 Gamma: PQ (参考γ={target_gamma})") # ← 新增 - self.log_gui.log("=" * 50) - - else: # screen_module - target_gamma = 2.2 - - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始测试色准(屏模组 标准 - 29色)") - self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}") - self.log_gui.log("=" * 50) - - # 获取 29色名称 - color_names = self.config.get_accuracy_color_names() - - self.log_gui.log(f"✓ 将测试 {len(color_names)} 个色块") - self.log_gui.log(f" 色块分组:") - self.log_gui.log(f" 灰阶 (5个): {', '.join(color_names[:5])}") - self.log_gui.log(f" ColorChecker (18个): {', '.join(color_names[5:23])}") - self.log_gui.log(f" 饱和色 (6个): {', '.join(color_names[23:])}") - - self.log_gui.log("=" * 50) - self.log_gui.log("开始发送色准图案并采集数据...") - self.log_gui.log("=" * 50) - - # 发送 29色图案 - measured_data_list = self.send_fix_pattern("accuracy") - - if measured_data_list is None or len(measured_data_list) != 29: - self.log_gui.log(f"❌ 数据数量不匹配") - self.log_gui.log(f" 期望: 29 个") - self.log_gui.log( - f" 实际: {len(measured_data_list) if measured_data_list else 0} 个" - ) - return - - # 保存原始测量数据供单步调试使用 - self.results.add_intermediate_data("accuracy", "measured", measured_data_list) - - # ========== 计算 ΔE 2000(显示 Gamma)========== - self.log_gui.log("=" * 50) - self.log_gui.log(f"计算色准(ΔE 2000,Gamma {target_gamma})...") - self.log_gui.log("=" * 50) - - # 获取标准 xy 坐标 - standards = self.get_accuracy_color_standards(test_type) - - delta_e_values = [] - color_patches = [] - - for i, (name, measured_data) in enumerate(zip(color_names, measured_data_list)): - measured_x = measured_data[0] - measured_y = measured_data[1] - measured_lv = measured_data[2] - - standard_x, standard_y = standards.get(name, (0.3127, 0.3290)) - - delta_e = self.calculate_delta_e_2000( - measured_x, - measured_y, - measured_lv, - standard_x, - standard_y, - ) - - delta_e_values.append(delta_e) - color_patches.append(name) - - if delta_e < 3: - grade, icon = "优秀", "✓" - elif delta_e < 5: - grade, icon = "良好", "○" - else: - grade, icon = "偏差", "✗" - - self.log_gui.log( - f" [{i+1:2d}] {name:20s} ΔE={delta_e:5.2f} {icon} {grade}" - ) - - # ========== 统计 ========== - avg_delta_e_all = sum(delta_e_values) / len(delta_e_values) - max_delta_e_all = max(delta_e_values) - min_delta_e_all = min(delta_e_values) - - excellent_count_all = sum(1 for de in delta_e_values if de < 3) - good_count_all = sum(1 for de in delta_e_values if 3 <= de < 5) - poor_count_all = sum(1 for de in delta_e_values if de >= 5) - - delta_e_gray = delta_e_values[0:5] - avg_delta_e_gray = sum(delta_e_gray) / len(delta_e_gray) - - delta_e_colorchecker = delta_e_values[5:23] - avg_delta_e_colorchecker = sum(delta_e_colorchecker) / len(delta_e_colorchecker) - - delta_e_saturated = delta_e_values[23:29] - avg_delta_e_saturated = sum(delta_e_saturated) / len(delta_e_saturated) - - self.log_gui.log("=" * 50) - self.log_gui.log("色准统计(全 29色):") - self.log_gui.log("=" * 50) - self.log_gui.log(f" 平均 ΔE: {avg_delta_e_all:.2f}") - self.log_gui.log(f" 最大 ΔE: {max_delta_e_all:.2f}") - self.log_gui.log(f" 最小 ΔE: {min_delta_e_all:.2f}") - self.log_gui.log(f" 优秀 (ΔE<3): {excellent_count_all} 个") - self.log_gui.log(f" 良好 (3≤ΔE<5): {good_count_all} 个") - self.log_gui.log(f" 偏差 (ΔE≥5): {poor_count_all} 个") - - self.log_gui.log("") - self.log_gui.log("分组统计:") - self.log_gui.log(f" 灰阶 (5个): 平均 ΔE = {avg_delta_e_gray:.2f}") - self.log_gui.log( - f" ColorChecker (18个): 平均 ΔE = {avg_delta_e_colorchecker:.2f}" - ) - self.log_gui.log(f" 饱和色 (6个): 平均 ΔE = {avg_delta_e_saturated:.2f}") - - # ========== 保存测试结果 ========== - accuracy_data = { - "color_patches": color_patches, - "delta_e_values": delta_e_values, - "color_measurements": measured_data_list, - "avg_delta_e": avg_delta_e_all, - "max_delta_e": max_delta_e_all, - "min_delta_e": min_delta_e_all, - "excellent_count": excellent_count_all, - "good_count": good_count_all, - "poor_count": poor_count_all, - "avg_delta_e_gray": avg_delta_e_gray, - "avg_delta_e_colorchecker": avg_delta_e_colorchecker, - "avg_delta_e_saturated": avg_delta_e_saturated, - "target_gamma": target_gamma, - } - - self.results.set_test_item_result("accuracy", accuracy_data) - - # ========== 绘制图表 ========== - self.plot_accuracy(accuracy_data, test_type) - - self.log_gui.log("色准测试完成") - - def get_accuracy_color_standards(self, test_type): - """转发到 app.tests.color_accuracy.get_accuracy_color_standards(Step 1 重构)""" - return _get_accuracy_color_standards(test_type) - - def calculate_gamut_coverage(self, results): - """转发到 app.tests.gamut.calculate_gamut_coverage(Step 1 重构)""" - return _calc_gamut_coverage(results) - - def calculate_gamma(self, results, max_index_fix, pattern_params=None): - """转发到 app.tests.gamma.calculate_gamma(Step 1 重构)""" - return _calc_gamma(results, max_index_fix, pattern_params) - - def calculate_color_accuracy(self, measured, standard): - """转发到 app.tests.color_accuracy.calculate_color_accuracy(Step 1 重构)""" - return _calc_color_accuracy(measured, standard) - - def plot_gamut(self, results, coverage, test_type): - """转发到 app.plots.plot_gamut.plot_gamut(Step 2 重构)""" - return _plot_gamut(self, results, coverage, test_type) - def plot_gamma(self, L_bar, results_with_gamma_list, target_gamma, test_type): - """转发到 app.plots.plot_gamma.plot_gamma(Step 2 重构)""" - return _plot_gamma(self, L_bar, results_with_gamma_list, target_gamma, test_type) - def plot_eotf(self, L_bar, results_with_eotf_list, test_type): - """转发到 app.plots.plot_eotf.plot_eotf(Step 2 重构)""" - return _plot_eotf(self, L_bar, results_with_eotf_list, test_type) - def calculate_pq_curve(self, gray_levels): - """转发到 app.tests.eotf.calculate_pq_curve(Step 1 重构)""" - return _calc_pq_curve(gray_levels) - - def plot_cct(self, test_type): - """转发到 app.plots.plot_cct.plot_cct(Step 2 重构)""" - return _plot_cct(self, test_type) - def plot_contrast(self, contrast_data, test_type): - """转发到 app.plots.plot_contrast.plot_contrast(Step 2 重构)""" - return _plot_contrast(self, contrast_data, test_type) - def plot_accuracy(self, accuracy_data, test_type): - """转发到 app.plots.plot_accuracy.plot_accuracy(Step 2 重构)""" - return _plot_accuracy(self, accuracy_data, test_type) - def on_test_completed(self): - """测试完成后的UI更新""" - self.testing = False - self.start_btn.config(state=tk.NORMAL) - self.stop_btn.config(state=tk.DISABLED) - self.save_btn.config(state=tk.NORMAL) - self.clear_config_btn.config(state=tk.NORMAL) - self.status_var.set("测试完成") - self.log_gui.log("测试完成") - - # 恢复配置项按钮 - if hasattr(self, "config_panel_frame"): - try: - self.config_panel_frame.btn.configure(state="normal") - except: - pass - - # 启用色域参考标准下拉框 - try: - test_type = self.config.current_test_type - - if test_type == "screen_module" and hasattr(self, "screen_gamut_combo"): - self.screen_gamut_combo.configure(state="readonly") - self.log_gui.log("✓ 屏模组色域参考标准已启用") - - elif test_type == "sdr_movie" and hasattr(self, "sdr_gamut_combo"): - self.sdr_gamut_combo.configure(state="readonly") - self.log_gui.log("✓ SDR 色域参考标准已启用") - - elif test_type == "hdr_movie" and hasattr(self, "hdr_gamut_combo"): - self.hdr_gamut_combo.configure(state="readonly") - self.log_gui.log("✓ HDR 色域参考标准已启用") - except Exception as e: - self.log_gui.log(f"启用色域参考标准失败: {str(e)}") - - # 获取当前测试类型和选中的测试项 - selected_items = self.get_selected_test_items() - test_type = self.config.current_test_type - - # ==================== ✅ 启用单步调试按钮 ==================== - if hasattr(self, "debug_panel"): - try: - # 屏模组:启用 Gamma 和 RGB 单步调试 - if test_type == "screen_module": - if "gamma" in selected_items: - gray_data = self.results.get_intermediate_data("shared", "gray") - if gray_data: - self.debug_panel.enable_debug( - "screen_module", "gamma", gray_data - ) - - # 启用 RGB 单步调试(色域测试完成后) - if "gamut" in selected_items: - rgb_data = self.results.get_intermediate_data("gamut", "rgb") - if rgb_data: - self.debug_panel.enable_debug( - "screen_module", "rgb", rgb_data - ) - - # ✅ 启用单步调试按钮 - if hasattr(self, "screen_debug_btn"): - self.screen_debug_btn.config(state=tk.NORMAL) - self.log_gui.log("✓ 屏模组单步调试按钮已启用") - - # SDR:启用 Gamma、色准和 RGB 单步调试 - elif test_type == "sdr_movie": - if "gamma" in selected_items: - gray_data = self.results.get_intermediate_data("shared", "gray") - if gray_data: - self.debug_panel.enable_debug( - "sdr_movie", "gamma", gray_data - ) - - if "accuracy" in selected_items: - accuracy_data = self.results.get_intermediate_data( - "accuracy", "measured" - ) - if accuracy_data: - self.debug_panel.enable_debug( - "sdr_movie", "accuracy", accuracy_data - ) - - # 启用 RGB 单步调试(色域测试完成后) - if "gamut" in selected_items: - rgb_data = self.results.get_intermediate_data("gamut", "rgb") - if rgb_data: - self.debug_panel.enable_debug("sdr_movie", "rgb", rgb_data) - - # ✅ 启用单步调试按钮 - if hasattr(self, "sdr_debug_btn"): - self.sdr_debug_btn.config(state=tk.NORMAL) - self.log_gui.log("✓ SDR 单步调试按钮已启用") - - # HDR:启用 EOTF、色准和 RGB 单步调试 - elif test_type == "hdr_movie": - if "eotf" in selected_items: - gray_data = self.results.get_intermediate_data("shared", "gray") - if gray_data: - self.debug_panel.enable_debug( - "hdr_movie", "eotf", gray_data - ) - - if "accuracy" in selected_items: - accuracy_data = self.results.get_intermediate_data( - "accuracy", "measured" - ) - if accuracy_data: - self.debug_panel.enable_debug( - "hdr_movie", "accuracy", accuracy_data - ) - - # 启用 RGB 单步调试(色域测试完成后) - if "gamut" in selected_items: - rgb_data = self.results.get_intermediate_data("gamut", "rgb") - if rgb_data: - self.debug_panel.enable_debug("hdr_movie", "rgb", rgb_data) - - # ✅ 启用单步调试按钮 - if hasattr(self, "hdr_debug_btn"): - self.hdr_debug_btn.config(state=tk.NORMAL) - self.log_gui.log("✓ HDR 单步调试按钮已启用") - - except Exception as e: - self.log_gui.log(f"启用单步调试失败: {str(e)}") - - # ==================== 显示色度/色域重新计算按钮 ==================== - if "cct" in selected_items: - try: - if test_type == "screen_module" and hasattr(self, "recalc_cct_btn"): - self.recalc_cct_btn.grid() - self.log_gui.log("✓ 屏模组色度参数调整按钮已启用") - elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_cct_btn"): - self.sdr_recalc_cct_btn.grid() - self.log_gui.log("✓ SDR 色度参数调整按钮已启用") - elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_cct_btn"): - self.hdr_recalc_cct_btn.grid() - self.log_gui.log("✓ HDR 色度参数调整按钮已启用") - except Exception as e: - self.log_gui.log(f"显示色度重新计算按钮失败: {str(e)}") - - if "gamut" in selected_items: - try: - if test_type == "screen_module" and hasattr(self, "recalc_gamut_btn"): - self.recalc_gamut_btn.grid() - self.log_gui.log("✓ 屏模组色域参考调整按钮已启用") - elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_gamut_btn"): - self.sdr_recalc_gamut_btn.grid() - self.log_gui.log("✓ SDR 色域参考调整按钮已启用") - elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_gamut_btn"): - self.hdr_recalc_gamut_btn.grid() - self.log_gui.log("✓ HDR 色域参考调整按钮已启用") - except Exception as e: - self.log_gui.log(f"显示色域重新计算按钮失败: {str(e)}") - - messagebox.showinfo("完成", "测试已完成!") - - def on_custom_template_test_completed(self): - """客户模板测试完成后的UI更新""" - self.testing = False - self.set_custom_result_table_locked(False) - self.start_btn.config(state=tk.NORMAL) - self.stop_btn.config(state=tk.DISABLED) - self.save_btn.config(state=tk.DISABLED) - self.clear_config_btn.config(state=tk.NORMAL) - self.custom_btn.config(state=tk.NORMAL) - self.status_var.set("客户模板测试完成") - - if hasattr(self, "config_panel_frame"): - try: - self.config_panel_frame.btn.configure(state="normal") - except: - pass - - self.log_gui.log("客户模板测试完成") - messagebox.showinfo("完成", "客户模板测试已完成!") - - def get_current_test_result(self): - """获取当前测试结果""" - test_type = self.test_type_var.get() - test_items = self.get_selected_test_items() - - # 构建测试结果字典 - result = { - "test_type": test_type, - "test_type_name": self.get_test_type_name(test_type), - "test_items": test_items, - "test_items_names": self.config.get_test_item_chinese_names(test_items), - "timestamp": datetime.datetime.now(), - "status": "完成", - "results": {}, - } - - # 根据测试项目收集结果数据 - for item in test_items: - if item == "gamut" and hasattr(self, "gamut_results"): - result["results"]["gamut"] = getattr(self, "gamut_results", {}) - elif item in ["gamma", "eotf"] and hasattr(self, "gamma_results"): - result["results"][item] = getattr(self, "gamma_results", {}) - elif item == "cct" and hasattr(self, "cct_results"): - result["results"]["cct"] = getattr(self, "cct_results", {}) - elif item == "contrast" and hasattr(self, "contrast_results"): - result["results"]["contrast"] = getattr(self, "contrast_results", {}) - elif item == "accuracy" and hasattr(self, "accuracy_results"): - result["results"]["accuracy"] = getattr(self, "accuracy_results", {}) - - return result - - def on_test_error(self): - """测试出错后的UI更新""" - self.testing = False - self.set_custom_result_table_locked(False) - self.start_btn.config(state=tk.NORMAL) - self.stop_btn.config(state=tk.DISABLED) - self.clear_config_btn.config(state=tk.NORMAL) - if hasattr(self, "custom_btn"): - self.custom_btn.config(state=tk.NORMAL) - self.status_var.set("测试出错") - - # 恢复配置项按钮 - if hasattr(self, "config_panel_frame"): - try: - self.config_panel_frame.btn.configure(state="normal") - except: - pass - - messagebox.showerror("错误", "测试过程中发生错误,请查看日志") - + new_pq_results = _run_new_pq_results + run_test = _run_run_test + run_screen_module_test = _run_run_screen_module_test + run_custom_sdr_test = _run_run_custom_sdr_test + run_sdr_movie_test = _run_run_sdr_movie_test + run_hdr_movie_test = _run_run_hdr_movie_test + send_fix_pattern = _run_send_fix_pattern + test_custom_sdr = _run_test_custom_sdr + test_gamut = _run_test_gamut + test_gamma = _run_test_gamma + test_eotf = _run_test_eotf + test_cct = _run_test_cct + test_contrast = _run_test_contrast + calculate_delta_e_2000 = staticmethod(_calc_delta_e_2000) + + test_color_accuracy = _run_test_color_accuracy + get_accuracy_color_standards = staticmethod(_get_accuracy_color_standards) + + calculate_gamut_coverage = staticmethod(_calc_gamut_coverage) + + calculate_gamma = staticmethod(_calc_gamma) + + calculate_color_accuracy = staticmethod(_calc_color_accuracy) + + plot_gamut = _plot_gamut + plot_gamma = _plot_gamma + plot_eotf = _plot_eotf + calculate_pq_curve = staticmethod(_calc_pq_curve) + + plot_cct = _plot_cct + plot_contrast = _plot_contrast + plot_accuracy = _plot_accuracy + on_test_completed = _run_on_test_completed + on_custom_template_test_completed = _run_on_custom_template_test_completed + get_current_test_result = _run_get_current_test_result + on_test_error = _run_on_test_error def get_test_type_name(self, test_type): """获取测试类型的显示名称""" if test_type == "screen_module": @@ -6355,12 +4902,8 @@ class PQAutomationApp: except Exception as e: self.log_gui.log(f"❌ 屏模组信号格式更改失败: {str(e)}") - def load_pq_config(self): - """转发到 app.config_io.load_pq_config(Step 4 重构)""" - return _cfg_load_pq_config(self) - def save_pq_config(self): - """转发到 app.config_io.save_pq_config(Step 4 重构)""" - return _cfg_save_pq_config(self) + load_pq_config = _cfg_load_pq_config + save_pq_config = _cfg_save_pq_config def on_closing(self): """窗口关闭时的处理""" try: @@ -6657,30 +5200,14 @@ class PQAutomationApp: self.log_gui.log("✓ HDR 单步调试面板已打开(独立窗口)") - def clear_config_file(self): - """转发到 app.config_io.clear_config_file(Step 4 重构)""" - return _cfg_clear_config_file(self) - def start_local_dimming_test(self): - """转发到 app.tests.local_dimming.start_local_dimming_test(Step 4 重构)""" - return _ld_start_local_dimming_test(self) - def update_ld_results(self, results): - """转发到 app.tests.local_dimming.update_ld_results(Step 4 重构)""" - return _ld_update_ld_results(self, results) - def stop_local_dimming_test(self): - """转发到 app.tests.local_dimming.stop_local_dimming_test(Step 4 重构)""" - return _ld_stop_local_dimming_test(self) - def send_ld_window(self, percentage): - """转发到 app.tests.local_dimming.send_ld_window(Step 4 重构)""" - return _ld_send_ld_window(self, percentage) - def measure_ld_luminance(self): - """转发到 app.tests.local_dimming.measure_ld_luminance(Step 4 重构)""" - return _ld_measure_ld_luminance(self) - def clear_ld_records(self): - """转发到 app.tests.local_dimming.clear_ld_records(Step 4 重构)""" - return _ld_clear_ld_records(self) - def save_local_dimming_results(self): - """转发到 app.tests.local_dimming.save_local_dimming_results(Step 4 重构)""" - return _ld_save_local_dimming_results(self) + clear_config_file = _cfg_clear_config_file + start_local_dimming_test = _ld_start_local_dimming_test + update_ld_results = _ld_update_ld_results + stop_local_dimming_test = _ld_stop_local_dimming_test + send_ld_window = _ld_send_ld_window + measure_ld_luminance = _ld_measure_ld_luminance + clear_ld_records = _ld_clear_ld_records + save_local_dimming_results = _ld_save_local_dimming_results def main(): diff --git a/settings/pq_config.json b/settings/pq_config.json index d7b733a..02b8611 100644 --- a/settings/pq_config.json +++ b/settings/pq_config.json @@ -1,5 +1,5 @@ { - "current_test_type": "screen_module", + "current_test_type": "sdr_movie", "test_types": { "screen_module": { "name": "屏模组性能测试",