继续优化色准测试结果显示模块
This commit is contained in:
@@ -1073,6 +1073,226 @@ def test_color_accuracy(self: "PQAutomationApp", test_type):
|
||||
self.log_gui.log("色准测试完成", level="success")
|
||||
|
||||
|
||||
def run_simulation_test(self: "PQAutomationApp"):
|
||||
"""运行模拟测试(无需 UCD/CA),直接在 UI 展示结果。"""
|
||||
try:
|
||||
test_type = self.config.current_test_type
|
||||
selected_items = self.get_selected_test_items()
|
||||
|
||||
if not selected_items:
|
||||
self.log_gui.log("未选择测试项目,无法执行模拟测试", level="error")
|
||||
messagebox.showwarning("提示", "请先勾选至少一个测试项目")
|
||||
return
|
||||
|
||||
self.log_gui.log("=" * 60, level="separator")
|
||||
self.log_gui.log("开始执行模拟测试(无需 UCD/CA 设备)", level="info")
|
||||
self.log_gui.log(f"测试类型: {self.get_test_type_name(test_type)}", level="info")
|
||||
self.log_gui.log(
|
||||
f"测试项目: {', '.join(self.config.get_test_item_chinese_names(selected_items))}",
|
||||
level="info",
|
||||
)
|
||||
|
||||
if hasattr(self, "update_chart_tabs_state"):
|
||||
self.update_chart_tabs_state()
|
||||
if hasattr(self, "clear_chart"):
|
||||
self.clear_chart()
|
||||
|
||||
self.new_pq_results(test_type, f"{self.get_test_type_name(test_type)} 模拟测试")
|
||||
self.status_var.set("模拟测试进行中...")
|
||||
|
||||
rng = np.random.default_rng()
|
||||
|
||||
def _read_ideal_xy():
|
||||
try:
|
||||
if test_type == "sdr_movie":
|
||||
return float(self.sdr_cct_x_ideal_var.get()), float(self.sdr_cct_y_ideal_var.get())
|
||||
if test_type == "hdr_movie":
|
||||
return float(self.hdr_cct_x_ideal_var.get()), float(self.hdr_cct_y_ideal_var.get())
|
||||
return float(self.cct_x_ideal_var.get()), float(self.cct_y_ideal_var.get())
|
||||
except Exception:
|
||||
return 0.3127, 0.3290
|
||||
|
||||
def _xyY_to_xyz_row(x, y, lv):
|
||||
if y <= 1e-8:
|
||||
return [x, y, lv, 0.0, lv, 0.0]
|
||||
X = x * lv / y
|
||||
Z = (1 - x - y) * lv / y
|
||||
return [x, y, lv, X, lv, Z]
|
||||
|
||||
# 共享灰阶数据:用于 Gamma/EOTF/CCT/对比度
|
||||
gray_results = []
|
||||
x_ideal, y_ideal = _read_ideal_xy()
|
||||
peak_lv = 900.0 if test_type == "hdr_movie" else 220.0
|
||||
gamma_shape = 2.25 if test_type == "hdr_movie" else 2.20
|
||||
|
||||
for i in range(11):
|
||||
p = i / 10.0
|
||||
lv = 0.08 + peak_lv * (p ** gamma_shape)
|
||||
lv *= 1.0 + float(rng.normal(0.0, 0.015))
|
||||
lv = max(lv, 0.03)
|
||||
|
||||
x = x_ideal + float(rng.normal(0.0, 0.0012))
|
||||
y = y_ideal + float(rng.normal(0.0, 0.0012))
|
||||
gray_results.append(_xyY_to_xyz_row(x, y, lv))
|
||||
|
||||
if any(item in selected_items for item in ("gamma", "eotf", "cct", "contrast")):
|
||||
self.results.add_intermediate_data("shared", "gray", gray_results)
|
||||
|
||||
# 色域模拟
|
||||
if "gamut" in selected_items:
|
||||
ref_map = {
|
||||
"BT.709": [(0.6400, 0.3300), (0.3000, 0.6000), (0.1500, 0.0600)],
|
||||
"DCI-P3": [(0.6800, 0.3200), (0.2650, 0.6900), (0.1500, 0.0600)],
|
||||
"BT.2020": [(0.7080, 0.2920), (0.1700, 0.7970), (0.1310, 0.0460)],
|
||||
"BT.601": [(0.6300, 0.3400), (0.3100, 0.5950), (0.1550, 0.0700)],
|
||||
}
|
||||
|
||||
if test_type == "hdr_movie":
|
||||
reference = self.hdr_gamut_ref_var.get() if hasattr(self, "hdr_gamut_ref_var") else "BT.2020"
|
||||
elif test_type == "sdr_movie":
|
||||
reference = self.sdr_gamut_ref_var.get() if hasattr(self, "sdr_gamut_ref_var") else "BT.709"
|
||||
else:
|
||||
reference = self.screen_gamut_ref_var.get() if hasattr(self, "screen_gamut_ref_var") else "DCI-P3"
|
||||
|
||||
if reference not in ref_map:
|
||||
reference = "DCI-P3"
|
||||
|
||||
gamut_results = []
|
||||
for rx, ry in ref_map[reference]:
|
||||
mx = rx + float(rng.normal(0.0, 0.006))
|
||||
my = ry + float(rng.normal(0.0, 0.006))
|
||||
gamut_results.append(_xyY_to_xyz_row(mx, my, 120.0))
|
||||
|
||||
self.results.add_intermediate_data("gamut", "rgb", gamut_results)
|
||||
self.results.set_test_item_result(
|
||||
"gamut",
|
||||
{
|
||||
"area": 0.0,
|
||||
"coverage": 95.0,
|
||||
"uv_coverage": 93.0,
|
||||
"reference": reference,
|
||||
},
|
||||
)
|
||||
self.plot_gamut(gamut_results, 95.0, test_type)
|
||||
|
||||
# Gamma / EOTF 模拟
|
||||
if "gamma" in selected_items and test_type != "hdr_movie":
|
||||
pattern_params = self.config.default_pattern_gray.get("pattern_params", None)
|
||||
results_with_gamma, L_bar = self.calculate_gamma(
|
||||
gray_results, len(gray_results) - 1, pattern_params
|
||||
)
|
||||
self.results.set_test_item_result("gamma", {"gamma": results_with_gamma, "L_bar": L_bar})
|
||||
try:
|
||||
target_gamma = float(self.sdr_gamma_type_var.get()) if test_type == "sdr_movie" else 2.2
|
||||
except Exception:
|
||||
target_gamma = 2.2
|
||||
self.plot_gamma(L_bar, results_with_gamma, target_gamma, test_type)
|
||||
|
||||
if "eotf" in selected_items and test_type == "hdr_movie":
|
||||
pattern_params = self.config.default_pattern_gray.get("pattern_params", None)
|
||||
results_with_eotf, L_bar = self.calculate_gamma(
|
||||
gray_results, len(gray_results) - 1, pattern_params
|
||||
)
|
||||
self.results.set_test_item_result("eotf", {"eotf": results_with_eotf, "L_bar": L_bar})
|
||||
self.plot_eotf(L_bar, results_with_eotf, test_type)
|
||||
|
||||
# CCT 模拟
|
||||
if "cct" in selected_items:
|
||||
cct_values = pq_algorithm.calculate_cct_from_results(gray_results)
|
||||
self.results.set_test_item_result("cct", {"cct_values": cct_values})
|
||||
self.plot_cct(test_type)
|
||||
|
||||
# 对比度模拟
|
||||
if "contrast" in selected_items:
|
||||
luminance_values = [row[2] for row in gray_results]
|
||||
max_luminance = max(luminance_values)
|
||||
min_luminance = max(min(luminance_values), 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.plot_contrast(contrast_data, test_type)
|
||||
|
||||
# 色准模拟
|
||||
if "accuracy" in selected_items:
|
||||
color_names = self.config.get_accuracy_color_names()
|
||||
standards = self.get_accuracy_color_standards(test_type)
|
||||
|
||||
color_patches = []
|
||||
measured_data = []
|
||||
delta_e_values = []
|
||||
|
||||
for idx, name in enumerate(color_names):
|
||||
sx, sy = standards.get(name, (0.3127, 0.3290))
|
||||
|
||||
# 前 20 个色块偏差更小,后 9 个稍大,方便 UI 看出差异
|
||||
noise_sigma = 0.0008 if idx < 20 else 0.0018
|
||||
mx = sx + float(rng.normal(0.0, noise_sigma))
|
||||
my = sy + float(rng.normal(0.0, noise_sigma))
|
||||
lv = max(5.0, 40.0 + idx * 2.3 + float(rng.normal(0.0, 4.0)))
|
||||
|
||||
row = _xyY_to_xyz_row(mx, my, lv)
|
||||
measured_data.append(row)
|
||||
color_patches.append(name)
|
||||
|
||||
delta_e = self.calculate_delta_e_2000(mx, my, lv, sx, sy)
|
||||
delta_e_values.append(delta_e)
|
||||
|
||||
avg_delta_e = float(np.mean(delta_e_values)) if delta_e_values else 0.0
|
||||
max_delta_e = float(np.max(delta_e_values)) if delta_e_values else 0.0
|
||||
min_delta_e = float(np.min(delta_e_values)) if delta_e_values else 0.0
|
||||
|
||||
excellent_count = sum(1 for d in delta_e_values if d < 3)
|
||||
good_count = sum(1 for d in delta_e_values if 3 <= d < 5)
|
||||
poor_count = sum(1 for d in delta_e_values if d >= 5)
|
||||
|
||||
delta_e_gray = delta_e_values[0:5]
|
||||
delta_e_colorchecker = delta_e_values[5:23]
|
||||
delta_e_saturated = delta_e_values[23:29]
|
||||
|
||||
try:
|
||||
target_gamma = float(self.sdr_gamma_type_var.get()) if test_type == "sdr_movie" else 2.2
|
||||
except Exception:
|
||||
target_gamma = 2.2
|
||||
|
||||
accuracy_data = {
|
||||
"color_patches": color_patches,
|
||||
"delta_e_values": delta_e_values,
|
||||
"color_measurements": measured_data,
|
||||
"avg_delta_e": avg_delta_e,
|
||||
"max_delta_e": max_delta_e,
|
||||
"min_delta_e": min_delta_e,
|
||||
"excellent_count": excellent_count,
|
||||
"good_count": good_count,
|
||||
"poor_count": poor_count,
|
||||
"avg_delta_e_gray": float(np.mean(delta_e_gray)) if delta_e_gray else 0.0,
|
||||
"avg_delta_e_colorchecker": float(np.mean(delta_e_colorchecker)) if delta_e_colorchecker else 0.0,
|
||||
"avg_delta_e_saturated": float(np.mean(delta_e_saturated)) if delta_e_saturated else 0.0,
|
||||
"target_gamma": target_gamma,
|
||||
}
|
||||
|
||||
self.results.add_intermediate_data("accuracy", "measured", measured_data)
|
||||
self.results.set_test_item_result("accuracy", accuracy_data)
|
||||
self.plot_accuracy(accuracy_data, test_type)
|
||||
|
||||
self.save_btn.config(state=tk.NORMAL)
|
||||
self.status_var.set("模拟测试完成")
|
||||
self.log_gui.log("模拟测试完成,结果已显示到 UI", level="success")
|
||||
self.log_gui.log("=" * 60, level="separator")
|
||||
messagebox.showinfo("完成", "模拟测试已完成(无需 UCD/CA)")
|
||||
|
||||
except Exception as e:
|
||||
self.status_var.set("模拟测试失败")
|
||||
self.log_gui.log(f"模拟测试失败: {str(e)}", level="error")
|
||||
import traceback
|
||||
self.log_gui.log(traceback.format_exc(), level="error")
|
||||
messagebox.showerror("错误", f"模拟测试失败: {str(e)}")
|
||||
|
||||
|
||||
def on_test_completed(self: "PQAutomationApp"):
|
||||
"""测试完成后的UI更新"""
|
||||
self.testing = False
|
||||
@@ -1310,6 +1530,7 @@ class TestRunnerMixin:
|
||||
test_cct = test_cct
|
||||
test_contrast = test_contrast
|
||||
test_color_accuracy = test_color_accuracy
|
||||
run_simulation_test = run_simulation_test
|
||||
on_test_completed = on_test_completed
|
||||
on_custom_template_test_completed = on_custom_template_test_completed
|
||||
get_current_test_result = get_current_test_result
|
||||
|
||||
Reference in New Issue
Block a user