From e959b1df06f29fe6542cc6cbe2f9005f620d5dc9 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 29 Sep 2026 11:18:01 +0800 Subject: p75: HUD 顶部条去整宽深色带(改紧凑半透明药丸) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HudLayout:BarTop/BarBottom alpha=0,新增 pillHairline 1px 发丝线与 pillHeight;DrawHudBar 不再铺整条底色与整宽 accent,改画每段半透明药丸+发丝线,图标+数值并排、右侧留白充足。布局/语义/快捷键不变,HUD 结构仅视觉层次。新增 P75HudStripTests(4 例);LV_P43_SHOT 同机位昼/夜证据 + analyze-p75(顶部条近黑 2.6% vs 整宽带 100%、无实心黑块、亮字仍在,verdict=PASS)。Desktop 354/354、Kernel 120/120、digest 与 p70 一致。 --- scripts/analyze-p75.py | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 scripts/analyze-p75.py (limited to 'scripts') diff --git a/scripts/analyze-p75.py b/scripts/analyze-p75.py new file mode 100644 index 0000000..e8f521d --- /dev/null +++ b/scripts/analyze-p75.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""P75(HUD 顶部条:去整宽深色带,改紧凑半透明药丸)证据分析:真实昼/夜同机位帧。 + +判据(读真实实跑日志 + 顶部条像素统计): + 1. 日志含 `p43-shot=p43-hud-day` 与 `p43-shot=p43-hud-night`(同机位,12:00 / 23:30)。 + 2. 顶部条区域(y 90%。 + 3. 顶部条内无 >=24x24 近黑块。 + 4. 文字可读:顶部条内亮像素(RGB 均 >=200)数量 > 300(标签/数值/图标仍在)。 + 5. 昼/夜两帧 pixel diff > 0。 + 6. 观感是否精美由 Hermes vision 侧复核,非本脚本判据。 + +Usage: python3 scripts/analyze-p75.py +Exit 0 pass / 1 fail / 2 usage. +""" + +import re +import sys +from pathlib import Path + +import numpy as np +from PIL import Image + +SHOT_RE = re.compile(r"^p43-shot=(p43-hud-(?:day|night))\S* tick=(\d+) calendar=(\S+)", re.MULTILINE) +BAR_HEIGHT = 36 +DARK_MAX = 40 +DARK_FRAC_MAX = 0.10 +BLOCK_SIDE = 24 +BRIGHT_MIN = 200 +BRIGHT_MIN_COUNT = 300 +FRAMES = ["p43-hud-day", "p43-hud-night"] + + +def max_block(mask, side): + h, w = mask.shape + if h < side or w < side: + return 0 + integral = np.zeros((h + 1, w + 1), dtype=np.int64) + integral[1:, 1:] = mask.astype(np.int64).cumsum(axis=0).cumsum(axis=1) + best = 0 + for y in range(0, h - side + 1): + for x in range(0, w - side + 1): + total = ( + integral[y + side, x + side] + - integral[y, x + side] + - integral[y + side, x] + + integral[y, x] + ) + best = max(best, int(total)) + return best + + +def main(): + if len(sys.argv) != 3: + print(__doc__) + return 2 + rec = Path(sys.argv[1]) + out = Path(sys.argv[2]) + log = (rec / "log.txt").read_text(encoding="utf-8", errors="replace") + failures = [] + lines = [ + "P75(HUD 顶部条去整宽深色带 → 紧凑半透明药丸)证据分析 — 真实 Desktop xvfb 1280x720", + "钩子:LV_P43_SHOT=1(同机位昼 12:00 / 夜 23:30)", + "判据:顶部条 y<36 近黑占比 vs 整宽深色带基线(100%)下降 >90%,文字亮像素仍在", + "", + ] + + shots = {m.group(1): m.group(0) for m in SHOT_RE.finditer(log)} + for name in FRAMES: + if name not in shots: + failures.append(f"log missing p43-shot={name}") + else: + lines.append("log: " + shots[name]) + lines.append("") + + baseline_px = 1280 * BAR_HEIGHT + stats = {} + for name in FRAMES: + path = rec / f"{name}.png" + if not path.exists(): + failures.append(f"missing frame {path.name}") + continue + rgb = np.asarray(Image.open(path).convert("RGB"), dtype=np.int16) + h, w = rgb.shape[0], rgb.shape[1] + strip = rgb[0:BAR_HEIGHT, :, :] + dark_mask = (strip[:, :, 0] <= DARK_MAX) & (strip[:, :, 1] <= DARK_MAX) & (strip[:, :, 2] <= DARK_MAX) + dark_px = int(dark_mask.sum()) + strip_px = strip.shape[0] * strip.shape[1] + dark_frac = dark_px / strip_px + drop = 1.0 - dark_frac + block = max_block(dark_mask, BLOCK_SIDE) + bright = int(((strip[:, :, 0] >= BRIGHT_MIN) & (strip[:, :, 1] >= BRIGHT_MIN) & (strip[:, :, 2] >= BRIGHT_MIN)).sum()) + stats[name] = {"dark_frac": dark_frac, "bright": bright} + lines.append( + f"{name}.png: size={w}x{h} strip_dark_px={dark_px}/{strip_px} dark_frac={dark_frac:.3f} drop_vs_full_band={drop*100:.1f}% dark_block_{BLOCK_SIDE}={block} bright_px={bright}" + ) + if (w, h) != (1280, 720): + failures.append(f"{path.name} size {w}x{h} != 1280x720") + if dark_frac >= DARK_FRAC_MAX: + failures.append(f"{name} strip dark_frac {dark_frac:.3f} >= {DARK_FRAC_MAX}") + if drop <= 0.90: + failures.append(f"{name} dark-area drop {drop*100:.1f}% <= 90%") + if block >= BLOCK_SIDE * BLOCK_SIDE: + failures.append(f"{name} has a solid {BLOCK_SIDE}x{BLOCK_SIDE} near-black block in strip") + if bright < BRIGHT_MIN_COUNT: + failures.append(f"{name} strip bright_px {bright} < {BRIGHT_MIN_COUNT} (text/icons may be unreadable)") + lines.append("") + + have = [n for n in FRAMES if n in stats] + if len(have) == 2: + a = np.asarray(Image.open(rec / f"{have[0]}.png").convert("RGB"), dtype=np.int16) + b = np.asarray(Image.open(rec / f"{have[1]}.png").convert("RGB"), dtype=np.int16) + differing = int((np.abs(a - b).sum(axis=2) > 0).sum()) + lines.append(f"diff {have[0]} vs {have[1]}: differing_pixels={differing}") + if differing == 0: + failures.append("day and night frames are identical") + + lines.append("") + if failures: + lines.append("verdict=FAIL") + for f in failures: + lines.append(" - " + f) + else: + lines.append("verdict=PASS") + lines.append(f" baseline full-width dark band = {baseline_px} px (frac 1.000).") + lines.append(" both strips are mostly scene/pill, no near-black block, and text/icons remain bright.") + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"wrote {out} verdict={'FAIL' if failures else 'PASS'}") + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) -- cgit v1.2.3