Spaces:
Paused
Paused
File size: 5,552 Bytes
f66643d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | """So sánh 3 cách MATCHING cho localization (class-agnostic) trên cùng dữ liệu:
(0) HIỆN TẠI — containment + union, hai chiều tách rời (recall-side / precision-side).
(A) COCO 1-1 — ghép 1-1 tham lam theo IoU giảm dần, mỗi GT/pred dùng 1 lần (chuẩn
detection; không có confidence nên sort theo IoU).
(B) COMPONENT — đồ thị chồng lấn (cạnh khi IoU>=.5 hoặc containment>=.5 hai chiều) →
thành phần liên thông; cụm "khớp" nếu IoU(union_GT, union_pred)>=t →
mọi GT/pred trong cụm tính TP (một phép ghép nhất quán, xử lý N-M).
Báo P/R/F1@IoU cho tổng thể + vài lát cắt. Chỉ để đối chiếu, KHÔNG thay eval_layout.
.venv/bin/python compare_matchers.py [--iou 0.5] [--granularity fine]
"""
from __future__ import annotations
import argparse, json
from collections import defaultdict
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
import eval_layout as E
# benchmark/parser/ — chứa data/ (GT) và parser_results/ (batch_*.json + mapping.json)
BASE = Path(__file__).resolve().parents[1]
# ---- (A) COCO 1-1 greedy ----
def coco_tp(gts, preds, t):
pairs = []
for i, g in enumerate(gts):
for j, p in enumerate(preds):
v = E.iou(g["box"], p["box"])
if v >= t:
pairs.append((v, i, j))
pairs.sort(reverse=True)
ug, up, tp = set(), set(), 0
for v, i, j in pairs:
if i in ug or j in up:
continue
ug.add(i); up.add(j); tp += 1
return tp, tp # tp_gt == tp_pred (ghép 1-1)
# ---- (B) connected components ----
def comp_tp(gts, preds, t):
parent = {}
def find(x):
parent.setdefault(x, x)
root = x
while parent[root] != root:
root = parent[root]
while parent[x] != root:
parent[x], x = root, parent[x]
return root
def union(a, b):
ra, rb = find(a), find(b)
if ra != rb:
parent[ra] = rb
for i in range(len(gts)):
find(("g", i))
for j in range(len(preds)):
find(("p", j))
for i, g in enumerate(gts):
for j, p in enumerate(preds):
gb, pb = g["box"], p["box"]
if (E.iou(gb, pb) >= 0.5 or E.contain_ratio(gb, pb) >= 0.5
or E.contain_ratio(pb, gb) >= 0.5):
union(("g", i), ("p", j))
comps = defaultdict(lambda: {"g": [], "p": []})
for i, g in enumerate(gts):
comps[find(("g", i))]["g"].append(g["box"])
for j, p in enumerate(preds):
comps[find(("p", j))]["p"].append(p["box"])
tp_g = tp_p = 0
for c in comps.values():
if not c["g"] or not c["p"]:
continue
if E.iou(E.union_box(c["g"]), E.union_box(c["p"])) >= t:
tp_g += len(c["g"]); tp_p += len(c["p"])
return tp_g, tp_p
# ---- (0) current two-sided ----
def cur_tp(gts, preds, t):
rec = E.match_side(gts, preds, 0.5)
prc = E.match_side(preds, gts, 0.5)
tp_g = sum(1 for r in rec if r["union_iou"] >= t)
tp_p = sum(1 for r in prc if r["union_iou"] >= t)
return tp_g, tp_p
def prf(tp_g, tp_p, n_gt, n_pred):
r = tp_g / n_gt if n_gt else 0.0
p = tp_p / n_pred if n_pred else 0.0
f = 2 * p * r / (p + r) if (p + r) else 0.0
return p, r, f
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--iou", type=float, default=0.5)
ap.add_argument("--granularity", default="fine")
ap.add_argument("--gt", type=Path, default=BASE / "data" / "OmniDocBench.json")
ap.add_argument("--mapping", type=Path,
default=BASE / "parser_results" / "mapping.json")
ap.add_argument("--pred", type=Path, default=BASE / "parser_results",
help="Thư mục chứa batch_*.json của parser.")
args = ap.parse_args()
t = args.iou
gt = E.load_gt(args.gt, args.granularity)
mp = json.load(open(args.mapping))
idx = E.build_pred_index(args.pred, mp)
METHODS = {"current": cur_tp, "coco_1to1": coco_tp, "component": comp_tp}
# acc[slice][method] = [tp_g, tp_p, n_gt, n_pred]
acc = defaultdict(lambda: {m: [0, 0, 0, 0] for m in METHODS})
for name, pp in idx.items():
g = gt.get(name)
if g is None:
continue
gts = E.prep_gt(g, True)
preds = E.prep_pred(pp)
keys = ["all"]
lang = g["attr"].get("language"); lay = g["attr"].get("layout")
if lang: keys.append(f"language={lang}")
if lay: keys.append(f"layout={lay}")
per = {}
for m, fn in METHODS.items():
per[m] = fn(gts, preds, t)
for k in keys:
for m in METHODS:
a = acc[k][m]
a[0] += per[m][0]; a[1] += per[m][1]
a[2] += len(gts); a[3] += len(preds)
def show(k):
print(f"\n### {k}")
print(f" {'method':10s} P@{t} R@{t} F1@{t}")
for m in METHODS:
tp_g, tp_p, n_gt, n_pred = acc[k][m]
p, r, f = prf(tp_g, tp_p, n_gt, n_pred)
print(f" {m:10s} {p:.3f} {r:.3f} {f:.3f}")
print(f"So sánh matching @IoU={t}, granularity={args.granularity}, pred={args.pred}")
show("all")
for k in ["layout=single_column", "layout=double_column", "layout=three_column",
"language=english", "language=simplified_chinese"]:
if k in acc:
show(k)
if __name__ == "__main__":
main()
|