修复UCD连接错误、修改色准计算方式、修改localdiming选择方块
This commit is contained in:
@@ -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 xy(X-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 -> Lab(CIE 官方定义)
|
||||
# ----------------------------------------------------------------------
|
||||
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 为参考白点的绝对 XYZ(Yn 通常为白场亮度 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
|
||||
):
|
||||
"""
|
||||
色准测试专用 ΔE2000(Calman 对齐)。
|
||||
|
||||
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}
|
||||
@@ -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}%亮度)发送失败",
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user