AsianPlayer/TriDrive / code /tools /review_tool /build_relabel_pass.py
AsianPlayer's picture
download
raw
5.32 kB
#!/usr/bin/env python3
"""tools/review_tool/build_relabel_pass.py — second, independent labelling pass for the automatic hand_return labels (owner request 2026-09-16:
"engaged hand_return 0.559 / manual hand_return 0.765 — 我有可能看错了, 请你让我重新标注一下").
Builds results/review_tool/{relabel_hr.html,items_relabel_hr.json} from the EXISTING items.json / img/ (no frame extraction, read-only on data):
* the 68 Task-B hand_return items (engaged 34 first, then manual 34), new ids <id>__r2 so the first-pass labels are neither shown nor overwritten;
* a separate localStorage key; server sync via the same /save and /labels endpoints of serve_review.py (port 8791);
* an explicit definition of hand_return and re-worded options.
First-pass labels stay in labels.jsonl / labels_latest.json (snapshot labels_latest_pass1_<utc>.json); apply_review.py --pass2 reads the __r2 labels.
"""
from __future__ import annotations
import argparse, json, sys, time
from pathlib import Path
CWM3 = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(Path(__file__).resolve().parent))
from build_review_tool import HTML # noqa: E402
SUFFIX = "__r2"
DEF_CN = ("<b>hand_return(手回到方向盘)</b>的定义:自动标签认为在<b>中间帧(dt = 0)</b>驾驶员的手从"
"<b>离开方向盘</b>变为<b>放回方向盘</b>。前两帧(−1.0 s、−0.5 s)应当至少一只手<b>不在</b>方向盘上;从中间帧起(0、+0.5、+1.0 s)手应当<b>握在/放在</b>方向盘上。"
"只看手,不看头部;红框只是标出驾驭员。这一页<b>不显示</b>你上次的答案,请独立判断;备注仍可写 phone 等。")
OPTS_B_NEW = ("B:[['correct','正确:前面手不在盘上,中间帧(±0.5 s 内)手回到盘上'],"
"['wrong','错误:中间帧附近没有“手回到盘上”(手一直在盘上 / 一直不在盘上 / 握手机或放在盘旁却没握盘)'],"
"['timing','时间偏差:确有回盘,但比中间帧早或晚超过 0.5 s'],['unsure','不确定 / 看不清']]")
def main(argv=None) -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--out", default=str(CWM3 / "results/review_tool"))
ap.add_argument("--event", default="hand_return")
a = ap.parse_args(argv)
out = Path(a.out)
src = json.load(open(out / "items.json", encoding="utf-8"))["items"]
sel = [i for i in src if i["task"] == "B" and i["meta"]["event"] == a.event]
sel.sort(key=lambda i: (0 if i["meta"]["regime"] == "engaged" else 1, i["id"]))
items = []
for i in sel:
m = dict(i["meta"]); m["pass"] = 2; m["orig_id"] = i["id"]
items.append({"id": i["id"] + SUFFIX, "task": "B", "meta": m, "frames": i["frames"]})
n_e = sum(i["meta"]["regime"] == "engaged" for i in items)
ts = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
json.dump({"built_utc": ts, "pass": 2, "event": a.event, "source_items": "items.json", "n_items": len(items), "items": items},
open(out / "items_relabel_hr.json", "w", encoding="utf-8"), indent=1, ensure_ascii=False)
html = HTML
reps = [
("const KEY = 'cwm3_review_v1';", "const KEY = 'cwm3_review_relabel_hr_v1';"),
("B:[['correct','正确:事件发生在中间帧附近'],['wrong','错误:没有该事件'],['timing','时间偏差 >0.5 s'],['unsure','不确定/看不清']]", OPTS_B_NEW),
("let tab = 'A', cur = 0;", "let tab = 'B', cur = 0;"),
("<title>CockpitWM 人工核验</title>", "<title>CockpitWM 复核:hand_return</title>"),
(" <b>CockpitWM 人工核验</b>\n <span class=\"tab active\" data-tab=\"A\" onclick=\"setTab('A')\">A 司机/乘客核验</span>\n <span class=\"tab\" data-tab=\"B\" onclick=\"setTab('B')\">B 自动标签验证</span>",
f" <b>CockpitWM 复核(第 2 轮):hand_return</b>\n <span class=\"tab active\" data-tab=\"B\">第一部分 engaged {n_e} 条 → 第二部分 manual {len(items) - n_e} 条</span>"),
("<main id=\"main\"></main>", f"<div class=\"item\" style=\"margin:12px 16px;max-width:1500px;background:#fff8e1;border-color:#e0b400\">{DEF_CN}</div>\n<main id=\"main\"></main>"),
(" if(r.rater && !store._rater) store._rater=r.rater;", ""), # do not inherit the first-pass rater string ('assistant' = server test ping)
("`自动标签:<b>${it.meta.event_cn}</b>,标注时刻 = 中间帧(EVENT,t=${it.meta.t_event.toFixed(1)} s,${it.meta.regime})。这个事件是否真实发生?`",
"`第 ${idx+1}/${items().length} 条(<b>${it.meta.regime}</b>)· 自动标签:<b>手回到方向盘</b>,标注时刻 = 中间帧(EVENT,t=${it.meta.t_event.toFixed(1)} s)。前两帧手不在盘上、从中间帧起手在盘上吗?`"),
]
for old, new in reps:
assert old in html, f"template anchor not found: {old[:60]}"
html = html.replace(old, new)
html = html.replace("__ITEMS__", json.dumps(items, ensure_ascii=False))
(out / "relabel_hr.html").write_text(html, encoding="utf-8")
print(f"relabel pass built: {len(items)} items ({n_e} engaged, {len(items) - n_e} manual) -> {out / 'relabel_hr.html'}, {out / 'items_relabel_hr.json'}")
return 0
if __name__ == "__main__":
sys.exit(main())

Xet Storage Details

Size:
5.32 kB
·
Xet hash:
b84dfaae4d002ea6d6e05fc2c16637525692cf47c6c962bf799f2813ed18d5e4

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.