Reorganize repository into pc web shared monorepo

Separate the local recognition, web publishing, and shared data paths while preserving direct script execution and existing site content.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
voson
2026-07-29 14:29:08 +08:00
co-authored by Cursor
parent 96a9312194
commit 9c5aa5b610
280 changed files with 1451 additions and 450 deletions
+76
View File
@@ -0,0 +1,76 @@
"""Score the recogniser against every labelled frame at once.
Labels live in samples/labels.json as {frame filename: 10 hero keys}, '?' for
slots nobody has identified yet. Those slots are skipped, not counted wrong.
Usage:
python evaluate.py
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import json
import cv2
from common import ROOT, load_config, load_template_library
from recognize import recognize_image
LABELS_PATH = ROOT / "samples" / "labels.json"
RAW_DIR = ROOT / "samples" / "raw"
def main() -> None:
if not LABELS_PATH.is_file():
sys.exit(f"missing {LABELS_PATH}")
frames = json.loads(LABELS_PATH.read_text(encoding="utf-8"))["frames"]
cfg = load_config()
if not cfg["slots"]:
sys.exit("config.json has no slots - run autocalibrate.py first")
library = load_template_library()
print(f"library: {len(library)} CDN templates")
graded = correct = skipped = 0
misses: list[str] = []
for name, truth in frames.items():
path = RAW_DIR / name
img = cv2.imread(str(path))
if img is None:
print(f" {name}: MISSING, skipped")
continue
result = recognize_image(img, cfg, library)
hits = frame_graded = 0
worst = 1.0
for slot, expected in zip(result["slots"], truth):
if expected == "?":
skipped += 1
continue
frame_graded += 1
worst = min(worst, slot["score"])
if slot["hero"] == expected:
hits += 1
else:
misses.append(
f" {name} slot {slot['slot']}: expected {expected}, "
f"got {slot['hero']} (raw {slot['raw_best']} "
f"score {slot['score']} margin {slot['margin']})"
)
graded += frame_graded
correct += hits
flag = " ranked" if result.get("ranked_overlay") else ""
print(f" {name}: {hits}/{frame_graded} lowest score {worst:.3f} {result['elapsed_ms']}ms{flag}")
if misses:
print("\nmisses:")
print("\n".join(misses))
pct = 100 * correct / graded if graded else 0
print(f"\ntotal: {correct}/{graded} ({pct:.1f}%){f', {skipped} unlabelled slots skipped' if skipped else ''}")
if __name__ == "__main__":
main()