summaryrefslogtreecommitdiff
path: root/scripts/analyze-p75.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/analyze-p75.py')
-rw-r--r--scripts/analyze-p75.py134
1 files changed, 134 insertions, 0 deletions
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<barHeight=36 全宽)近黑像素(RGB 均 <=40)占比 < 10%,
+ 相对「整宽深色带」基线(占比 100%)下降 > 90%。
+ 3. 顶部条内无 >=24x24 近黑块。
+ 4. 文字可读:顶部条内亮像素(RGB 均 >=200)数量 > 300(标签/数值/图标仍在)。
+ 5. 昼/夜两帧 pixel diff > 0。
+ 6. 观感是否精美由 Hermes vision 侧复核,非本脚本判据。
+
+Usage: python3 scripts/analyze-p75.py <record-dir> <out-txt>
+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())