Files
pqAutomationApp/tools/demo_accuracy_plot.py

188 lines
5.1 KiB
Python
Raw Normal View History

2026-05-27 14:58:44 +08:00
"""离线色准图 Demo。
运行后会在 tools/demo_outputs/ 下生成一张 PNG
用于在没有 UCD 设备时预览当前色准图表的 Calman 风格布局
"""
from __future__ import annotations
import argparse
import math
import sys
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.family"] = ["sans-serif"]
plt.rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from app.plots.plot_accuracy import plot_accuracy
from app.tests.color_accuracy import (
calculate_delta_e_2000,
get_accuracy_color_standards,
)
COLOR_NAMES = [
"White",
"Gray 80",
"Gray 65",
"Gray 50",
"Gray 35",
"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% Red",
"100% Green",
"100% Blue",
"100% Cyan",
"100% Magenta",
"100% Yellow",
]
class _DummyNotebook:
def select(self, *_args, **_kwargs):
return None
class _DummyCanvas:
def draw(self):
return None
class _DemoApp:
def __init__(self, fig):
self.accuracy_fig = fig
self.accuracy_canvas = _DummyCanvas()
self.chart_notebook = _DummyNotebook()
self.accuracy_chart_frame = object()
def get_test_type_name(self, test_type):
mapping = {
"sdr_movie": "SDR Movie",
"hdr_movie": "HDR Movie",
"screen_module": "屏模组",
}
return mapping.get(test_type, str(test_type))
def _build_demo_data(test_type: str = "sdr_movie"):
standards = get_accuracy_color_standards(test_type)
rng = np.random.default_rng(20260527)
measured = []
color_patches = []
delta_e_values = []
for idx, name in enumerate(COLOR_NAMES):
sx, sy = standards[name]
# 构造一些“看起来像真实测量”的偏移:
# 大部分点轻微偏移,少数点更明显,便于看出方向和等级差异。
if idx < 5:
offset_scale = 0.0012
elif idx < 23:
offset_scale = 0.0028
else:
offset_scale = 0.0045
angle = rng.uniform(0, 2 * math.pi)
radius = offset_scale * (0.55 + 0.85 * rng.random())
dx = math.cos(angle) * radius
dy = math.sin(angle) * radius
# 为了让图上连线不完全随机,给部分饱和色再加一点定向偏移。
if idx >= 23:
dx += 0.002 * (1 if idx % 2 == 0 else -1)
dy += 0.0015 * (1 if idx % 3 == 0 else -1)
mx = min(max(sx + dx, 0.0), 0.8)
my = min(max(sy + dy, 0.0), 0.9)
# 亮度也做一点微小变化,避免所有点完全同一层。
measured_lv = 70.0 + rng.normal(0, 4.0)
measured_lv = max(measured_lv, 1.0)
delta_e = calculate_delta_e_2000(mx, my, measured_lv, sx, sy)
measured.append((mx, my, measured_lv))
color_patches.append(name)
delta_e_values.append(delta_e)
avg_delta_e = float(np.mean(delta_e_values))
max_delta_e = float(np.max(delta_e_values))
min_delta_e = float(np.min(delta_e_values))
return {
"color_patches": color_patches,
"delta_e_values": delta_e_values,
"color_measurements": measured,
"avg_delta_e": avg_delta_e,
"max_delta_e": max_delta_e,
"min_delta_e": min_delta_e,
"excellent_count": sum(1 for value in delta_e_values if value < 3),
"good_count": sum(1 for value in delta_e_values if 3 <= value < 5),
"poor_count": sum(1 for value in delta_e_values if value >= 5),
"avg_delta_e_gray": float(np.mean(delta_e_values[0:5])),
"avg_delta_e_colorchecker": float(np.mean(delta_e_values[5:23])),
"avg_delta_e_saturated": float(np.mean(delta_e_values[23:29])),
"target_gamma": 2.2,
}
def main():
parser = argparse.ArgumentParser(description="Generate an offline color accuracy demo PNG.")
parser.add_argument(
"--output",
type=Path,
default=Path(__file__).resolve().parent / "demo_outputs" / "accuracy_demo.png",
help="Output PNG path.",
)
parser.add_argument(
"--test-type",
choices=["sdr_movie", "hdr_movie", "screen_module"],
default="sdr_movie",
help="Test type used for the title and standard color set.",
)
args = parser.parse_args()
args.output.parent.mkdir(parents=True, exist_ok=True)
fig = plt.Figure(figsize=(14, 8), dpi=120, tight_layout=False)
app = _DemoApp(fig)
accuracy_data = _build_demo_data(args.test_type)
plot_accuracy(app, accuracy_data, args.test_type)
fig.savefig(args.output, dpi=220)
print(f"Saved demo image to: {args.output}")
if __name__ == "__main__":
main()