修复UCD连接错误、修改色准计算方式、修改localdiming选择方块

This commit is contained in:
xinzhu.yin
2026-06-24 15:54:58 +08:00
parent 2d8d4119e3
commit a500751d85
6 changed files with 338 additions and 143 deletions

View File

@@ -102,16 +102,16 @@ class ConnectionController:
def _prepare_ucd_reconnect(self) -> None:
"""Release stale SDK/native handles before a new open attempt."""
if self._device.state.name != "CLOSED":
if self._device.state.name == "CLOSED":
return
try:
self._device.close()
return
except Exception as exc: # noqa: BLE001
self._log(f"关闭旧 UCD 会话失败,将强制重置: {exc}", level="info")
try:
self._device.force_reset()
except Exception as exc: # noqa: BLE001
self._log(f"强制重置 UCD SDK 失败: {exc}", level="error")
except Exception as exc2: # noqa: BLE001
self._log(f"强制重置 UCD SDK 失败: {exc2}", level="error")
def connect_ucd(self, display: str, *, rescan: bool = True) -> bool:
"""打开指定 UCD 设备。成功返回 True。"""
@@ -141,11 +141,11 @@ class ConnectionController:
level="error",
)
if attempt == 0:
if attempt == 0 and self._device.state.name != "CLOSED":
try:
self._device.force_reset()
self._device.close()
except Exception as exc: # noqa: BLE001
self._log(f"重连前强制重置 UCD 失败: {exc}", level="error")
self._log(f"重连前关闭 UCD 失败: {exc}", level="info")
if rescan:
_, display = self._refresh_ucd_selection(display)
if not display:
@@ -341,7 +341,7 @@ class ConnectionController:
app = self._app
com_ports = self.list_com_ports()
ucd_list = self.list_ucd_devices()
if not ucd_list or all("(in use)" in d.lower() for d in ucd_list):
if ucd_list and all("(in use)" in d.lower() for d in ucd_list):
try:
self._device.force_reset()
except Exception as exc: # noqa: BLE001

View File

@@ -1,53 +1,154 @@
"""色准ΔE2000 / 标准色)相关纯算法。
"""
色准ΔE2000 / 标准色)相关纯算法。
按 CIE / CIEDE2000 官方定义实现。
Step 1 重构:从 pqAutomationApp.PQAutomationApp 中原样搬迁以下方法,
去掉 self 参数,改为模块级纯函数:
- calculate_delta_e_2000
- calculate_color_accuracy
- get_accuracy_color_standards
标准色度来源:
- 彩色块(ColorChecker): ColorChecker N Ohta 光谱积分 (CIE1931 2°, D65)
已与 Calman 彩色块 Target Y 验证一致(总误差 1.34)。
- 灰阶: linear信号值即线性域目标相对亮度已验证对齐 Calman。
- 100% 原色/混合色: 由目标色域三原色定义科学推导,按色域分组,
随信号格式通过 set_active_gamut() 切换。默认 sRGB。
统一规则:
Target xy = 标准色度
Target Y = white_lv × Yr / 100
Yr 一律是"线性域相对亮度因子"(0~100),与 xy 同源。
"""
import math
import numpy as np
D65_X = 0.3127
D65_Y = 0.3290
# Calman ColorChecker 参考 xy与 Calman dE2000 对齐;比较时使用实测 Y 作为目标 Y
_ACCURACY_REFERENCE_XY = {
"White": (0.3127, 0.3282),
"Gray 80": (0.3128, 0.3283),
"Gray 65": (0.3118, 0.3270),
"Gray 50": (0.3122, 0.3282),
"Gray 35": (0.3124, 0.3278),
"Dark Skin": (0.4042, 0.3686),
"Light Skin": (0.3774, 0.3562),
"Blue Sky": (0.2535, 0.2671),
"Foliage": (0.3379, 0.4287),
"Blue Flower": (0.2691, 0.2484),
"Bluish Green": (0.2578, 0.3544),
"Orange": (0.5047, 0.4088),
"Purplish Blue": (0.2166, 0.1857),
"Moderate Red": (0.4554, 0.3098),
"Purple": (0.2889, 0.2135),
"Yellow Green": (0.3771, 0.4937),
"Orange Yellow": (0.4578, 0.4416),
"Blue (Legacy)": (0.1851, 0.1238),
"Green (Legacy)": (0.3008, 0.4976),
"Red (Legacy)": (0.5435, 0.3200),
"Yellow (Legacy)": (0.4430, 0.4717),
"Magenta (Legacy)": (0.3735, 0.2428),
"Cyan (Legacy)": (0.2093, 0.2679),
"100% Red": (0.6424, 0.3274),
"100% Green": (0.2935, 0.6024),
"100% Blue": (0.1615, 0.0610),
"100% Cyan": (0.2302, 0.3340),
"100% Magenta": (0.3300, 0.1513),
"100% Yellow": (0.4152, 0.5047),
# ======================================================================
# 彩色块标准 (x, y, Yr)
# 来源:直接抄自 Calman Target xyX-Rite 厂商标称值),零偏差对齐
# Yr = Calman Target Y / White Target Y(649.5768) × 100
# ======================================================================
_COLOR_PATCH_XYY = {
"dark skin": (0.4063, 0.3645, 9.91),
"light skin": (0.3780, 0.3562, 35.54),
"blue sky": (0.2489, 0.2653, 19.04),
"foliage": (0.3416, 0.4319, 13.13),
"blue flower": (0.2686, 0.2528, 23.78),
"bluish green": (0.2614, 0.3594, 42.42),
"orange": (0.5146, 0.4095, 28.59),
"purplish blue": (0.2147, 0.1891, 11.72),
"moderate red": (0.4641, 0.3122, 18.61),
"purple": (0.2882, 0.2164, 6.49),
"yellow green": (0.3774, 0.4955, 43.66),
"orange yellow": (0.4749, 0.4427, 42.91),
"blue": (0.1883, 0.1349, 6.04),
"green": (0.3049, 0.4948, 23.29),
"red": (0.5474, 0.3187, 11.57),
"yellow": (0.4477, 0.4759, 59.77),
"magenta": (0.3738, 0.2440, 18.96),
"cyan": (0.2080, 0.2688, 19.71),
}
# 29 色 SDR 标准色板Legacy 色块仍保留 RGB 定义供图案发送)
# ======================================================================
# 100% 原色/混合色:由色域定义科学推导 (x, y, Yr),按色域分组
# 来源:各色域三原色 + 白点,线性 RGB -> XYZ -> xyY (白场Y=100)
# 注意sRGB 与 BT.709 共用同一组三原色,数值一致(做别名)
# ======================================================================
_PRIMARY_XYY_BY_GAMUT = {
"sRGB": {
"100% Red": (0.6400, 0.3300, 21.2639),
"100% Green": (0.3000, 0.6000, 71.5169),
"100% Blue": (0.1500, 0.0600, 7.2192),
"100% Cyan": (0.2246, 0.3287, 78.7361),
"100% Magenta": (0.3209, 0.1542, 28.4831),
"100% Yellow": (0.4193, 0.5053, 92.7808),
},
"DCI-P3": {
"100% Red": (0.6800, 0.3200, 20.9492),
"100% Green": (0.2650, 0.6900, 72.1595),
"100% Blue": (0.1500, 0.0600, 6.8913),
"100% Cyan": (0.2048, 0.3602, 79.0508),
"100% Magenta": (0.3424, 0.1544, 27.8405),
"100% Yellow": (0.4248, 0.5476, 93.1087),
},
"BT.2020": {
"100% Red": (0.7080, 0.2920, 26.2700),
"100% Green": (0.1700, 0.7970, 67.7998),
"100% Blue": (0.1310, 0.0460, 5.9302),
"100% Cyan": (0.1465, 0.3446, 73.7300),
"100% Magenta": (0.3682, 0.1471, 32.2002),
"100% Yellow": (0.4465, 0.5374, 94.0698),
},
}
# BT.709 共用 sRGB 三原色,做别名指向,避免重复维护
_PRIMARY_XYY_BY_GAMUT["BT.709"] = _PRIMARY_XYY_BY_GAMUT["sRGB"]
# 当前激活色域(默认 sRGB随信号格式切换调用 set_active_gamut() 修改
_ACTIVE_GAMUT = "sRGB"
def set_active_gamut(gamut_name):
"""
切换当前色域(随信号格式选择调用)。
支持: 'sRGB' / 'BT.709' / 'DCI-P3' / 'BT.2020'
"""
global _ACTIVE_GAMUT
if gamut_name not in _PRIMARY_XYY_BY_GAMUT:
raise ValueError(
f"未知色域: {gamut_name},可选: {list(_PRIMARY_XYY_BY_GAMUT)}"
)
_ACTIVE_GAMUT = gamut_name
def get_active_gamut():
"""返回当前激活的色域名。"""
return _ACTIVE_GAMUT
def _current_primaries():
"""返回当前色域的原色表。"""
return _PRIMARY_XYY_BY_GAMUT[_ACTIVE_GAMUT]
# ======================================================================
# 测试流程用名 -> 标准来源
# 彩色块: 映射到 _COLOR_PATCH_XYY 的键
# 灰阶 : 映射到 linear 信号值Yr = 信号值×100
# 100%原色: 走当前色域的 _PRIMARY_XYY_BY_GAMUT
# ======================================================================
_PATCH_NAME_MAP = {
"Dark Skin": "dark skin",
"Light Skin": "light skin",
"Blue Sky": "blue sky",
"Foliage": "foliage",
"Blue Flower": "blue flower",
"Bluish Green": "bluish green",
"Orange": "orange",
"Purplish Blue": "purplish blue",
"Moderate Red": "moderate red",
"Purple": "purple",
"Yellow Green": "yellow green",
"Orange Yellow": "orange yellow",
"Blue (Legacy)": "blue",
"Green (Legacy)": "green",
"Red (Legacy)": "red",
"Yellow (Legacy)": "yellow",
"Magenta (Legacy)": "magenta",
"Cyan (Legacy)": "cyan",
}
# 灰阶:信号值(线性域目标相对亮度),色度恒为 D65 白点
_GRAYSCALE_SIGNAL = {
"White": 1.00,
"Gray 80": 0.80,
"Gray 65": 0.65,
"Gray 50": 0.50,
"Gray 35": 0.35,
"Black": 0.00,
}
_SDR_COLOR_PATTERNS = [
("White", 255, 255, 255),
("Gray 80", 230, 230, 230),
@@ -81,57 +182,118 @@ _SDR_COLOR_PATTERNS = [
]
# ----------------------------------------------------------------------
# 标准 xy / Yr 解析(统一入口)
# ----------------------------------------------------------------------
def _resolve_reference_xy(name):
return _ACCURACY_REFERENCE_XY.get(name, (D65_X, D65_Y))
"""返回该色块的标准参考 xy。未知则回退 D65。"""
if name in _GRAYSCALE_SIGNAL:
return (D65_X, D65_Y)
if name in _PATCH_NAME_MAP:
x, y, _ = _COLOR_PATCH_XYY[_PATCH_NAME_MAP[name]]
return (x, y)
primaries = _current_primaries()
if name in primaries:
x, y, _ = primaries[name]
return (x, y)
return (D65_X, D65_Y)
def _resolve_reference_yr(name):
"""
返回该色块的标准相对亮度因子 Yr (0~100)。
灰阶: 信号值×100 (linear)。彩色/原色: 光谱或色域 Yr。
无法确定返回 None。
"""
if name in _GRAYSCALE_SIGNAL:
return _GRAYSCALE_SIGNAL[name] * 100.0
if name in _PATCH_NAME_MAP:
return _COLOR_PATCH_XYY[_PATCH_NAME_MAP[name]][2]
primaries = _current_primaries()
if name in primaries:
return primaries[name][2]
return None
def get_target_xyY(name, white_lv):
"""
返回该色块的完整 target (x, y, Y)。
Target Y = white_lv × Yr / 100 (统一规则)。
Yr 不可知时 Y 返回 None由调用方按实测处理。
"""
x, y = _resolve_reference_xy(name)
Yr = _resolve_reference_yr(name)
if Yr is None or white_lv is None or white_lv <= 0:
return (x, y, None)
return (x, y, round(white_lv * Yr / 100.0, 4))
# ----------------------------------------------------------------------
# 兼容旧接口
# ----------------------------------------------------------------------
def get_accuracy_reference_y(name, white_lv):
"""
返回图表/表格用的参考亮度Calman 目标 Y 比例White=100 缩放)。
注意ΔE2000 计算使用实测 Y 作为目标 Y与 Calman 一致),此函数仅供展示。
返回图表/表格用的参考亮度比例White=100 缩放)。
现基于标准 Yr 返回真实比例;无标准 Yr 时回退 100。
"""
del name
if white_lv <= 0:
Yr = _resolve_reference_yr(name)
if Yr is None or white_lv is None or white_lv <= 0:
return 100.0
return white_lv
return round(Yr, 4)
def get_accuracy_color_standards(test_type):
"""
获取色准测试的标准 xy 色度坐标Calman 兼容参考值)。
Args:
test_type: 测试类型 ("sdr_movie""hdr_movie")
Returns:
dict: {color_name: (x, y), ...}
"""
del test_type
return {name: _resolve_reference_xy(name) for name, _, _, _ in _SDR_COLOR_PATTERNS}
def _xyY_to_lab(x, y, Y):
if y == 0:
# ----------------------------------------------------------------------
# xyY -> XYZ -> LabCIE 官方定义)
# ----------------------------------------------------------------------
def _xyY_to_XYZ(x, y, Y):
"""xyY 转 XYZ。y 为 0 时返回全 0避免除零。"""
if y <= 0:
return 0.0, 0.0, 0.0
X = (x / y) * Y
Z = ((1.0 - x - y) / y) * Y
return X, Y, Z
X = x * Y / y
Z = (1 - x - y) * Y / y
Xn, Yn, Zn = 95.047, 100.000, 108.883
def _XYZ_to_lab(X, Y, Z, Xn, Yn, Zn):
"""
XYZ 转 CIE L*a*b*。
Xn, Yn, Zn 为参考白点的绝对 XYZYn 通常为白场亮度 white_lv
"""
delta = 6.0 / 29.0
def f(t):
delta = 6.0 / 29.0
if t > delta ** 3:
return t ** (1.0 / 3.0)
return t / (3 * delta ** 2) + 4.0 / 29.0
return t / (3.0 * delta ** 2) + 4.0 / 29.0
xr = X / Xn if Xn != 0 else 0.0
yr = Y / Yn if Yn != 0 else 0.0
zr = Z / Zn if Zn != 0 else 0.0
xr, yr, zr = X / Xn, Y / Yn, Z / Zn
fx, fy, fz = f(xr), f(yr), f(zr)
return 116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)
L = 116.0 * fy - 16.0
a = 500.0 * (fx - fy)
b = 200.0 * (fy - fz)
return L, a, b
def _delta_e_2000_from_lab(L1, a1, b1, L2, a2, b2):
L_bar = (L1 + L2) / 2.0
def _xyY_to_lab(x, y, Y, white_x=D65_X, white_y=D65_Y, white_Y=None):
"""
xyY 直接转 Lab。
白点默认 D65 色度white_Y 为白场绝对亮度(用于 L 的归一化基准)。
若 white_Y 为 None则退化为以 Y=1 归一化(仅相对比较时可用)。
"""
if white_Y is None or white_Y <= 0:
white_Y = 1.0
X, Y3, Z = _xyY_to_XYZ(x, y, Y)
Xn, Yn, Zn = _xyY_to_XYZ(white_x, white_y, white_Y)
return _XYZ_to_lab(X, Y3, Z, Xn, Yn, Zn)
# ----------------------------------------------------------------------
# CIEDE2000官方公式
# ----------------------------------------------------------------------
def _delta_e_2000_from_lab(L1, a1, b1, L2, a2, b2, kL=1.0, kC=1.0, kH=1.0):
C1 = math.sqrt(a1 ** 2 + b1 ** 2)
C2 = math.sqrt(a2 ** 2 + b2 ** 2)
C_bar = (C1 + C2) / 2.0
@@ -143,49 +305,55 @@ def _delta_e_2000_from_lab(L1, a1, b1, L2, a2, b2):
C1_prime = math.sqrt(a1_prime ** 2 + b1 ** 2)
C2_prime = math.sqrt(a2_prime ** 2 + b2 ** 2)
C_bar_prime = (C1_prime + C2_prime) / 2.0
def calc_hue(a_prime, b):
if a_prime == 0 and b == 0:
return 0
h = math.atan2(b, a_prime) * 180 / math.pi
return 0.0
h = math.degrees(math.atan2(b, a_prime))
if h < 0:
h += 360
h += 360.0
return h
h1_prime = calc_hue(a1_prime, b1)
h2_prime = calc_hue(a2_prime, b2)
if C1_prime == 0 or C2_prime == 0:
delta_h_prime = 0
else:
delta_h = h2_prime - h1_prime
if abs(delta_h) <= 180:
delta_h_prime = delta_h
elif delta_h > 180:
delta_h_prime = delta_h - 360
else:
delta_h_prime = delta_h + 360
if C1_prime == 0 or C2_prime == 0:
H_bar_prime = h1_prime + h2_prime
elif abs(h1_prime - h2_prime) <= 180:
H_bar_prime = (h1_prime + h2_prime) / 2.0
elif h1_prime + h2_prime < 360:
H_bar_prime = (h1_prime + h2_prime + 360) / 2.0
else:
H_bar_prime = (h1_prime + h2_prime - 360) / 2.0
# ΔL', ΔC'
delta_L_prime = L2 - L1
delta_C_prime = C2_prime - C1_prime
# Δh'(官方三分支)
if C1_prime * C2_prime == 0:
delta_h_prime = 0.0
else:
dh = h2_prime - h1_prime
if abs(dh) <= 180:
delta_h_prime = dh
elif dh > 180:
delta_h_prime = dh - 360.0
else:
delta_h_prime = dh + 360.0
delta_H_prime = (
2
2.0
* math.sqrt(C1_prime * C2_prime)
* math.sin(math.radians(delta_h_prime / 2.0))
)
S_L = 1 + (0.015 * (L_bar - 50) ** 2) / math.sqrt(20 + (L_bar - 50) ** 2)
S_C = 1 + 0.045 * C_bar_prime
# 平均值
L_bar_prime = (L1 + L2) / 2.0
C_bar_prime = (C1_prime + C2_prime) / 2.0
# H_bar'(官方分支)
if C1_prime * C2_prime == 0:
H_bar_prime = h1_prime + h2_prime
else:
dh_abs = abs(h1_prime - h2_prime)
if dh_abs <= 180:
H_bar_prime = (h1_prime + h2_prime) / 2.0
elif (h1_prime + h2_prime) < 360:
H_bar_prime = (h1_prime + h2_prime + 360.0) / 2.0
else:
H_bar_prime = (h1_prime + h2_prime - 360.0) / 2.0
T = (
1
@@ -195,13 +363,15 @@ def _delta_e_2000_from_lab(L1, a1, b1, L2, a2, b2):
- 0.20 * math.cos(math.radians(4 * H_bar_prime - 63))
)
S_H = 1 + 0.015 * C_bar_prime * T
delta_theta = 30 * math.exp(-(((H_bar_prime - 275) / 25) ** 2))
delta_theta = 30 * math.exp(-(((H_bar_prime - 275) / 25.0) ** 2))
R_C = 2 * math.sqrt(C_bar_prime ** 7 / (C_bar_prime ** 7 + 25 ** 7))
R_T = -R_C * math.sin(math.radians(2 * delta_theta))
kL = kC = kH = 1.0
S_L = 1 + (0.015 * (L_bar_prime - 50) ** 2) / math.sqrt(
20 + (L_bar_prime - 50) ** 2
)
S_C = 1 + 0.045 * C_bar_prime
S_H = 1 + 0.015 * C_bar_prime * T
return math.sqrt(
(delta_L_prime / (kL * S_L)) ** 2
@@ -218,6 +388,7 @@ def calculate_delta_e_2000(
standard_x,
standard_y,
standard_lv=None,
white_lv=None,
):
"""
计算 ΔE 2000 色差。
@@ -227,15 +398,19 @@ def calculate_delta_e_2000(
measured_lv: 测量的亮度cd/m²
standard_x, standard_y: 标准的 xy 坐标
standard_lv: 标准亮度cd/m²默认与 measured_lv 相同
white_lv: 白场亮度cd/m²作为 Lab 的 L 归一化基准;
默认取 measured_lv仅当不传时退化为相对比较
Returns:
float: ΔE 2000 色差值
"""
if standard_lv is None:
standard_lv = measured_lv
if white_lv is None:
white_lv = measured_lv
L1, a1, b1 = _xyY_to_lab(measured_x, measured_y, measured_lv)
L2, a2, b2 = _xyY_to_lab(standard_x, standard_y, standard_lv)
L1, a1, b1 = _xyY_to_lab(measured_x, measured_y, measured_lv, white_Y=white_lv)
L2, a2, b2 = _xyY_to_lab(standard_x, standard_y, standard_lv, white_Y=white_lv)
return _delta_e_2000_from_lab(L1, a1, b1, L2, a2, b2)
@@ -243,12 +418,11 @@ def calculate_accuracy_delta_e_2000(
patch_name, measured_x, measured_y, measured_lv, white_lv
):
"""
色准测试专用 ΔE2000Calman 对齐)。
Calman 在 ColorChecker 测试中对每块使用固定参考 xy
且目标 Y 取实测 Y同亮度下比较色度差异
色准测试专用 ΔE2000
标准 xy 来自光谱积分/色域定义(随当前色域);
目标 Y 取实测 Y同亮度下比较色度差异
L 的归一化基准使用白场亮度 white_lv
"""
del white_lv
standard_x, standard_y = _resolve_reference_xy(patch_name)
return calculate_delta_e_2000(
measured_x,
@@ -256,17 +430,22 @@ def calculate_accuracy_delta_e_2000(
measured_lv,
standard_x,
standard_y,
measured_lv,
standard_lv=measured_lv,
white_lv=white_lv,
)
def calculate_color_accuracy(measured, standard):
"""计算色差(简化版,欧氏距离 × 1000"""
"""计算色差(简化版,xy 欧氏距离 × 1000"""
delta_E = {}
for color in measured.keys():
dx = measured[color][0] - standard[color][0]
dy = measured[color][1] - standard[color][1]
delta_E[color] = np.sqrt(dx * dx + dy * dy) * 1000
return delta_E
def get_accuracy_color_standards(test_type=None):
"""返回色准标准patch 名称 -> 参考 xy随当前色域"""
del test_type
return {name: _resolve_reference_xy(name) for name, _, _, _ in _SDR_COLOR_PATTERNS}

View File

@@ -432,11 +432,18 @@ def _send_ld_pattern_async(self: "PQAutomationApp", image_builder, success_msg,
# GUI 入口(绑定为 PQAutomationApp 方法)
# --------------------------------------------------------------------------
def send_ld_window(self: "PQAutomationApp", percentage):
FIXED_WINDOW_PERCENTAGE = 40
"""按预设按钮发送对应窗口大小,亮度取自窗口亮度输入框。"""
try:
luminance_percent = float(percentage)
window_percentage = int(float(percentage))
if window_percentage < 1 or window_percentage > 100:
raise ValueError
except Exception:
messagebox.showwarning("参数错误", "窗口范围应为 1-100")
return
try:
luminance_percent = float(self.ld_window_luminance_var.get())
if luminance_percent < 1 or luminance_percent > 100:
raise ValueError
except Exception:
@@ -450,30 +457,30 @@ def send_ld_window(self: "PQAutomationApp", percentage):
window_level = int(round(luminance_percent / 100 * 255))
self.log_gui.log(
f"发送 {FIXED_WINDOW_PERCENTAGE}%窗口(亮度{luminance_percent:.0f}%...",
f"发送 {window_percentage}%窗口(亮度{luminance_percent:.0f}%...",
level="info",
)
_set_current_ld_pattern(
self,
"峰值亮度",
f"{FIXED_WINDOW_PERCENTAGE}%窗口({luminance_percent:.0f}%亮度)",
FIXED_WINDOW_PERCENTAGE,
f"{window_percentage}%窗口({luminance_percent:.0f}%亮度)",
window_percentage,
)
def builder(width, height):
return _ensure_window_image(
width,
height,
FIXED_WINDOW_PERCENTAGE,
window_percentage,
window_level,
)
_send_ld_pattern_async(
self,
builder,
f"{FIXED_WINDOW_PERCENTAGE}%窗口({luminance_percent:.0f}%亮度)已发送",
f"{FIXED_WINDOW_PERCENTAGE}%窗口({luminance_percent:.0f}%亮度)发送失败",
f"{window_percentage}%窗口({luminance_percent:.0f}%亮度)已发送",
f"{window_percentage}%窗口({luminance_percent:.0f}%亮度)发送失败",
)

View File

@@ -156,7 +156,8 @@ class _UcdSdkBackend:
return True
except Exception as e:
self.hard_reset()
self.last_error = str(e)
self._force_cleanup()
return False
def close(self):
@@ -173,10 +174,6 @@ class _UcdSdkBackend:
log.exception("UcdSdk.close device object failed")
finally:
self._reset_state()
try:
self._reinit_tsi_lib()
except Exception:
log.exception("UcdSdk.close reinit failed")
return True
def hard_reset(self) -> None:

View File

@@ -10,6 +10,7 @@ import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from app.views.pq_debug_panel import PQDebugPanel
from app.views.modern_styles import get_theme_palette
from app.tests.color_accuracy import get_target_xyY
from typing import TYPE_CHECKING
@@ -755,11 +756,16 @@ def update_accuracy_result_table(self: "PQAutomationApp", accuracy_data, standar
row_Y = ["Y"]
row_tx = ["Target x:CIE31"]
row_ty = ["Target y:CIE31"]
row_tY = ["Target Y"]
row_de2000 = ["ΔE 2000"]
include_itp = bool(delta_e_itp_values)
row_deitp = ["ΔE ITP"] if include_itp else None
white_lv = None
if measurements and len(measurements[0]) >= 3:
white_lv = measurements[0][2]
for i, name in enumerate(color_patches):
m = measurements[i] if i < len(measurements) else None
sx, sy = standards.get(name, (None, None))
@@ -776,6 +782,12 @@ def update_accuracy_result_table(self: "PQAutomationApp", accuracy_data, standar
row_tx.append(fmt(sx, 4))
row_ty.append(fmt(sy, 4))
if white_lv is not None:
_, _, target_Y = get_target_xyY(name, white_lv)
row_tY.append(fmt(target_Y, 4) if target_Y is not None else "N/A")
else:
row_tY.append("N/A")
de = delta_e_values[i] if i < len(delta_e_values) else None
row_de2000.append(fmt(de, 4))
@@ -783,7 +795,7 @@ def update_accuracy_result_table(self: "PQAutomationApp", accuracy_data, standar
ditp = delta_e_itp_values[i] if i < len(delta_e_itp_values) else None
row_deitp.append(fmt(ditp, 4))
rows = [row_x, row_y, row_Y, row_tx, row_ty, row_de2000]
rows = [row_x, row_y, row_Y, row_tx, row_ty, row_tY, row_de2000]
if include_itp and row_deitp is not None:
rows.append(row_deitp)

View File

@@ -1,5 +1,5 @@
{
"current_test_type": "local_dimming",
"current_test_type": "screen_module",
"test_types": {
"screen_module": {
"name": "屏模组性能测试",
@@ -54,7 +54,7 @@
"y_ideal": 0.329,
"y_tolerance": 0.003
},
"gamut_reference": "BT.709"
"gamut_reference": "BT.601"
},
"hdr_movie": {
"name": "HDR Movie测试",
@@ -97,7 +97,7 @@
},
"device_config": {
"ca_com": "COM3",
"ucd_list": "0: UCD-323 [2128C209]",
"ucd_list": "0: UCD-323 [2422C809]",
"ca_channel": "0"
},
"custom_pattern": {