1.1.0版本

This commit is contained in:
xinzhu.yin
2026-04-16 16:51:05 +08:00
commit c157e774e5
333 changed files with 70759 additions and 0 deletions

601
utils/pq/pq_config.py Normal file
View File

@@ -0,0 +1,601 @@
# PQ自动化测试配置模块
import json
import copy
class PQConfig:
def __init__(self, current_test_type="screen_module", device_config={}, pattern={}):
self.default_test_types = {
"screen_module": {
"name": "屏模组性能测试",
"test_items": ["gamut", "gamma", "cct", "contrast"],
"timing": "DMT 1920x 1080 @ 60Hz",
"color_format": "RGB",
"bpc": 8,
"colorimetry": "sRGB",
},
"sdr_movie": {
"name": "SDR Movie测试",
"test_items": ["gamut", "gamma", "cct", "contrast", "accuracy"],
"timing": "DMT 1920x 1080 @ 60Hz",
"color_format": "RGB",
"bpc": 8,
"colorimetry": "sRGB",
},
"hdr_movie": {
"name": "HDR Movie测试",
"test_items": ["gamut", "eotf", "cct", "contrast", "accuracy"],
"timing": "DMT 1920x 1080 @ 60Hz",
"color_format": "RGB",
"bpc": 8,
"colorimetry": "sRGB",
},
}
# 设备连接配置
self.device_config = {
"ca_com": "COM1",
"ucd_list": "0: UCD-323 [2128C209]",
"ca_channel": "0",
}
# ========== RGB Pattern 配置 ==========
self.default_pattern_rgb = {
"pattern_mode": "SolidColor",
"measurement_bit_depth": 8,
"measurement_max_value": 2,
"pattern_params": [
[255, 0, 0], # 红色
[0, 255, 0], # 绿色
[0, 0, 255], # 蓝色
],
}
# ========== 灰阶 Pattern 配置 ==========
self.default_pattern_gray = {
"pattern_mode": "SolidColor",
"measurement_bit_depth": 8,
"measurement_max_value": 10,
"pattern_params": [
[255, 255, 255], # 100% 白色
[230, 230, 230], # 90%
[205, 205, 205], # 80%
[179, 179, 179], # 70%
[154, 154, 154], # 60%
[128, 128, 128], # 50%
[102, 102, 102], # 40%
[78, 78, 78], # 30%
[52, 52, 52], # 20%
[26, 26, 26], # 10%
[0, 0, 0], # 0% 黑色
],
}
# ========== 色准 Pattern 配置29色 - SDR 和 HDR 通用)==========
self.default_pattern_accuracy = {
"pattern_mode": "SolidColor",
"measurement_bit_depth": 8,
"measurement_max_value": 28, # 29个颜色最大索引是28
"pattern_params": [
# ========== 灰阶 (5个) ==========
[255, 255, 255], # 0: White
[230, 230, 230], # 1: Gray 80
[209, 209, 209], # 2: Gray 65
[186, 186, 186], # 3: Gray 50
[158, 158, 158], # 4: Gray 35
# ========== ColorChecker 24色 (18个) ==========
[115, 82, 66], # 5: Dark Skin
[194, 150, 130], # 6: Light Skin
[94, 122, 156], # 7: Blue Sky
[89, 107, 66], # 8: Foliage
[130, 128, 176], # 9: Blue Flower
[99, 189, 168], # 10: Bluish Green
[217, 120, 41], # 11: Orange
[74, 92, 163], # 12: Purplish Blue
[194, 84, 97], # 13: Moderate Red
[92, 61, 107], # 14: Purple
[158, 186, 64], # 15: Yellow Green
[230, 161, 46], # 16: Orange Yellow
[51, 61, 150], # 17: Blue (Legacy)
[71, 148, 71], # 18: Green (Legacy)
[176, 48, 59], # 19: Red (Legacy)
[237, 199, 33], # 20: Yellow (Legacy)
[186, 84, 145], # 21: Magenta (Legacy)
[0, 133, 163], # 22: Cyan (Legacy)
# ========== 100% 饱和色 (6个) ==========
[255, 0, 0], # 23: 100% Red
[0, 255, 0], # 24: 100% Green
[0, 0, 255], # 25: 100% Blue
[0, 255, 255], # 26: 100% Cyan
[255, 0, 255], # 27: 100% Magenta
[255, 255, 0], # 28: 100% Yellow
],
}
self.default_pattern_temp = {
"pattern_mode": "SolidColor",
"measurement_bit_depth": 8,
"measurement_max_value": 146,
"pattern_params": [
[255, 255, 255],
[242, 242, 242],
[230, 230, 230],
[217, 217, 217],
[204, 204, 204],
[191, 191, 191],
[179, 179, 179],
[166, 166, 166],
[153, 153, 153],
[140, 140, 140],
[128, 128, 128],
[115, 115, 115],
[102, 102, 102],
[89, 89, 89],
[77, 77, 77],
[64, 64, 64],
[51, 51, 51],
[38, 38, 38],
[26, 26, 26],
[13, 13, 13],
[0, 0, 0],
[255, 0, 0],
[242, 0, 0],
[230, 0, 0],
[217, 0, 0],
[204, 0, 0],
[191, 0, 0],
[179, 0, 0],
[166, 0, 0],
[153, 0, 0],
[140, 0, 0],
[128, 0, 0],
[115, 0, 0],
[102, 0, 0],
[89, 0, 0],
[77, 0, 0],
[64, 0, 0],
[51, 0, 0],
[38, 0, 0],
[26, 0, 0],
[13, 0, 0],
[0, 0, 0],
[0, 255, 0],
[0, 242, 0],
[0, 230, 0],
[0, 217, 0],
[0, 204, 0],
[0, 191, 0],
[0, 179, 0],
[0, 166, 0],
[0, 153, 0],
[0, 140, 0],
[0, 128, 0],
[0, 115, 0],
[0, 102, 0],
[0, 89, 0],
[0, 77, 0],
[0, 64, 0],
[0, 51, 0],
[0, 38, 0],
[0, 26, 0],
[0, 13, 0],
[0, 0, 0],
[0, 0, 255],
[0, 0, 242],
[0, 0, 230],
[0, 0, 217],
[0, 0, 204],
[0, 0, 191],
[0, 0, 179],
[0, 0, 166],
[0, 0, 153],
[0, 0, 140],
[0, 0, 128],
[0, 0, 115],
[0, 0, 102],
[0, 0, 89],
[0, 0, 77],
[0, 0, 64],
[0, 0, 51],
[0, 0, 38],
[0, 0, 26],
[0, 0, 13],
[0, 0, 0],
[255, 255, 0],
[242, 242, 0],
[230, 230, 0],
[217, 217, 0],
[204, 204, 0],
[191, 191, 0],
[179, 179, 0],
[166, 166, 0],
[153, 153, 0],
[140, 140, 0],
[128, 128, 0],
[115, 115, 0],
[102, 102, 0],
[89, 89, 0],
[77, 77, 0],
[64, 64, 0],
[51, 51, 0],
[38, 38, 0],
[26, 26, 0],
[13, 13, 0],
[0, 0, 0],
[0, 255, 255],
[0, 242, 242],
[0, 230, 230],
[0, 217, 217],
[0, 204, 204],
[0, 191, 191],
[0, 179, 179],
[0, 166, 166],
[0, 153, 153],
[0, 140, 140],
[0, 128, 128],
[0, 115, 115],
[0, 102, 102],
[0, 89, 89],
[0, 77, 77],
[0, 64, 64],
[0, 51, 51],
[0, 38, 38],
[0, 26, 26],
[0, 13, 13],
[0, 0, 0],
[255, 0, 255],
[242, 0, 242],
[230, 0, 230],
[217, 0, 217],
[204, 0, 204],
[191, 0, 191],
[179, 0, 179],
[166, 0, 166],
[153, 0, 153],
[140, 0, 140],
[128, 0, 128],
[115, 0, 115],
[102, 0, 102],
[89, 0, 89],
[77, 0, 77],
[64, 0, 64],
[51, 0, 51],
[38, 0, 38],
[26, 0, 26],
[13, 0, 13],
[0, 0, 0],
]
}
# 自定义图案
self.custom_pattern = {
"pattern_mode": "SolidColor",
"measurement_bit_depth": 8,
"measurement_max_value": 0,
"pattern_params": [],
}
self.current_test_types = self.default_test_types
self.current_test_type = current_test_type
self.current_pattern = self.default_pattern_rgb
# ========== 获取临时配置(用于 Full/Limited 转换)==========
def get_temp_config_with_converted_params(self, mode, converted_params):
"""
创建一个临时配置对象,包含转换后的 pattern 参数
Args:
mode: "rgb" | "gray" | "accuracy"
converted_params: 转换后的参数列表Full 或 Limited Range
Returns:
PQConfig: 临时配置对象(深拷贝,不影响原始配置)
"""
# 1. 深拷贝整个配置对象
temp_config = copy.deepcopy(self)
# 2. 设置正确的 pattern 模式
if mode == "rgb":
temp_config.current_pattern = copy.deepcopy(self.default_pattern_rgb)
elif mode == "gray":
temp_config.current_pattern = copy.deepcopy(self.default_pattern_gray)
elif mode == "accuracy":
temp_config.current_pattern = copy.deepcopy(self.default_pattern_accuracy)
# 3. 替换为转换后的参数
temp_config.current_pattern["pattern_params"] = converted_params
return temp_config
def to_dict(self):
"""将配置转换为字典格式"""
return {
"current_test_type": self.current_test_type,
"test_types": self.current_test_types,
"device_config": self.device_config,
"default_pattern_rgb": self.default_pattern_rgb,
"default_pattern_gray": self.default_pattern_gray,
"default_pattern_accuracy": self.default_pattern_accuracy,
"custom_pattern": self.custom_pattern,
}
def from_dict(self, config_dict):
"""从字典加载配置"""
self.current_test_type = config_dict.get("current_test_type", "screen_module")
self.current_test_types = config_dict.get("test_types", self.current_test_types)
self.device_config = config_dict.get("device_config", self.device_config)
self.default_pattern_rgb = config_dict.get(
"default_pattern_rgb", self.default_pattern_rgb
)
self.default_pattern_gray = config_dict.get(
"default_pattern_gray", self.default_pattern_gray
)
# ========== ✅ 强制使用新的 29色配置 ==========
loaded_accuracy = config_dict.get("default_pattern_accuracy", None)
# 检查加载的配置是否是旧的 10色
if loaded_accuracy and len(loaded_accuracy.get("pattern_params", [])) != 29:
print(
f"⚠️ 检测到旧的配置({len(loaded_accuracy.get('pattern_params', []))}色),强制使用新的 29色配置"
)
# 使用 __init__ 中定义的新配置
self.default_pattern_accuracy = self.default_pattern_accuracy
else:
self.default_pattern_accuracy = config_dict.get(
"default_pattern_accuracy", self.default_pattern_accuracy
)
# ==========================================
self.custom_pattern = config_dict.get("custom_pattern", self.custom_pattern)
def save_to_file(self, filename):
"""将配置保存到文件"""
with open(filename, "w", encoding="utf-8") as f:
json.dump(self.to_dict(), f, indent=4, ensure_ascii=False)
def set_current_test_type(self, test_type):
"""设置当前测试类型"""
if test_type in self.current_test_types:
self.current_test_type = test_type
return True
return False
def set_current_test_items(self, test_items):
"""设置当前测试类型的测试项"""
if self.current_test_type in self.current_test_types:
self.current_test_types[self.current_test_type]["test_items"] = test_items
return True
return False
def set_current_timing(self, timing):
if self.current_test_type in self.current_test_types:
self.current_test_types[self.current_test_type]["timing"] = timing
return True
return False
def set_device_config(self, ca_com, ucd_list, ca_channel):
"""设置设备连接配置"""
self.device_config["ca_com"] = ca_com
self.device_config["ucd_list"] = ucd_list
self.device_config["ca_channel"] = ca_channel
return True
def set_current_pattern(self, mode):
"""设置当前模式的测试图案"""
if mode == "rgb":
self.current_pattern = self.default_pattern_rgb
elif mode == "gray":
self.current_pattern = self.default_pattern_gray
elif mode == "accuracy": # ✅ 色准模式SDR 和 HDR 通用 29色
self.current_pattern = self.default_pattern_accuracy
elif mode == "custom":
# self.current_pattern = self.custom_pattern
self.current_pattern = self.default_pattern_temp
else:
return False
# 确保 measurement_max_value 是整数
if "measurement_max_value" in self.current_pattern:
value = self.current_pattern["measurement_max_value"]
if isinstance(value, str):
try:
self.current_pattern["measurement_max_value"] = int(value)
except ValueError:
self.current_pattern["measurement_max_value"] = (
len(self.current_pattern["pattern_params"]) - 1
)
return True
def set_custom_pattern(self, pattern_mode, pattern_params):
"""设置自定义模式的测试项"""
self.custom_pattern["pattern_mode"] = pattern_mode
self.custom_pattern["pattern_params"] = pattern_params
self.custom_pattern["measurement_max_value"] = len(pattern_params) - 1
return True
# ========== ✅ 获取 29色名称列表 ==========
def get_accuracy_color_names(self):
"""
获取色准测试的 29个颜色名称SDR 和 HDR 通用)
Returns:
list: 29个颜色名称
"""
return [
# 灰阶 (5个)
"White",
"Gray 80",
"Gray 65",
"Gray 50",
"Gray 35",
# ColorChecker 24色 (18个)
"Dark Skin",
"Light Skin",
"Blue Sky",
"Foliage",
"Blue Flower",
"Bluish Green",
"Orange",
"Purplish Blue",
"Moderate Red",
"Purple",
"Yellow Green",
"Orange Yellow",
"Blue (Legacy)",
"Green (Legacy)",
"Red (Legacy)",
"Yellow (Legacy)",
"Magenta (Legacy)",
"Cyan (Legacy)",
# 100% 饱和色 (6个)
"100% Red",
"100% Green",
"100% Blue",
"100% Cyan",
"100% Magenta",
"100% Yellow",
]
# ========== ✅ 获取 29色的 RGB 值 ==========
def get_accuracy_color_rgb(self):
"""
获取色准测试的 RGB 值(用于标准值计算)
Returns:
list: [(name, r, g, b), ...]
"""
names = self.get_accuracy_color_names()
rgb_values = self.default_pattern_accuracy["pattern_params"]
return [(name, r, g, b) for name, (r, g, b) in zip(names, rgb_values)]
def get_temp_pattern_names(self):
"""获取客户模板测试default_pattern_temp的固定 pattern 名称列表"""
percentages = list(range(100, -1, -5))
color_prefixes = ["W", "R", "G", "B", "Y", "C", "M"]
names = []
for prefix in color_prefixes:
for value in percentages:
names.append(f"{prefix} {value}%")
pattern_count = len(self.default_pattern_temp.get("pattern_params", []))
if pattern_count <= len(names):
return names[:pattern_count]
# 兜底:如果后续扩展了 pattern 数量,补充通用名称,避免索引越界。
for i in range(len(names), pattern_count):
names.append(f"P {i + 1}")
return names
def get_test_item_chinese_names(self, test_items):
"""获取测试项目的显示名称"""
item_names = []
for item in test_items:
if item == "gamut":
item_names.append("色域")
elif item == "gamma":
item_names.append("Gamma")
elif item == "eotf":
item_names.append("EOTF")
elif item == "cct":
item_names.append("色度一致性")
elif item == "contrast":
item_names.append("对比度")
elif item == "accuracy":
item_names.append("色准")
else:
item_names.append(item)
return item_names
def get_current_config(self):
"""返回当前测试类型相关的所有配置信息"""
if self.current_test_type not in self.current_test_types:
return {}
current_test = self.current_test_types[self.current_test_type]
config_info = {
"test_type": self.current_test_type,
"test_name": current_test.get("name", "未知测试"),
"test_items": current_test.get("test_items", []),
"test_items_chinese": self.get_test_item_chinese_names(
current_test.get("test_items", [])
),
"timing": current_test.get("timing", "DMT 1920x 1080 @ 60Hz"),
"color_format": current_test.get("color_format", "RGB"),
"bpc": current_test.get("bpc", 8),
"colorimetry": current_test.get("colorimetry", "sRGB"),
}
return config_info
# ========== ✅ 验证代码(测试完成后可删除)==========
if __name__ == "__main__":
print("=" * 60)
print("验证 pq_config.py 配置")
print("=" * 60)
config = PQConfig()
# 检查 default_pattern_accuracy
pattern_count = len(config.default_pattern_accuracy["pattern_params"])
max_value = config.default_pattern_accuracy["measurement_max_value"]
print(f"\ndefault_pattern_accuracy:")
print(f" 图案数量: {pattern_count}")
print(f" measurement_max_value: {max_value}")
if pattern_count == 29 and max_value == 28:
print("\n✅ 配置正确29色")
# 显示前 5 个图案
print("\n前5个图案:")
for i in range(5):
rgb = config.default_pattern_accuracy["pattern_params"][i]
names = config.get_accuracy_color_names()
print(f" [{i}] {names[i]:15s} RGB{rgb}")
# 显示后 5 个图案
print("\n后5个图案:")
for i in range(24, 29):
rgb = config.default_pattern_accuracy["pattern_params"][i]
names = config.get_accuracy_color_names()
print(f" [{i}] {names[i]:15s} RGB{rgb}")
# 测试 set_current_pattern
print("\n测试 set_current_pattern('accuracy'):")
config.set_current_pattern("accuracy")
current_count = len(config.current_pattern["pattern_params"])
print(f" current_pattern 图案数量: {current_count}")
if current_count == 29:
print(" ✅ set_current_pattern 工作正常")
else:
print(f" ❌ set_current_pattern 失败!只有 {current_count} 个图案")
else:
print(f"\n❌ 配置错误!")
print(f" 期望: 29 个图案, measurement_max_value=28")
print(f" 实际: {pattern_count} 个图案, measurement_max_value={max_value}")
print("\n❌ 请检查 default_pattern_accuracy 定义!")
print(" 应该包含:")
print(" - 5个灰阶")
print(" - 18个 ColorChecker 色块")
print(" - 6个 100% 饱和色")
print(" - 总计 29 个 RGB 数组")
print("=" * 60)