Eroux commited on
Commit
bf5ab25
·
verified ·
1 Parent(s): 3efb1b0

Upload infer.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. infer.py +93 -0
infer.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Run the Tibetan modern-book layout detector (RF-DETR-L fine-tune) on one or
3
+ more page images, applying the recommended *per-class* confidence thresholds.
4
+
5
+ The model is a 4-class RF-DETR-L (header, text-area, footnote, footer),
6
+ fine-tuned on the same `tam2col` labels as BDRC's primary RT-DETR-l release
7
+ (see the model card / blog post). Like that model, the detector is
8
+ deliberately recall-happy on the small marginal header/footer boxes, so the
9
+ single best operating point differs by class. The thresholds below are each
10
+ class's own max-F1 confidence from a native per-class sweep on the held-out
11
+ test set (same methodology as the primary RT-DETR-l release); a single global
12
+ 0.30 is the best compromise if you need one number for all classes.
13
+
14
+ Usage:
15
+ python infer.py --checkpoint rfdetr_tibetan_book_layout.pth --source page.jpg
16
+ python infer.py --checkpoint rfdetr_tibetan_book_layout.pth --source pages/ --out preds
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ from pathlib import Path
22
+
23
+ IMG_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tif", ".tiff"}
24
+
25
+ # Per-class max-F1 operating points (see model card). Use --global-conf 0.30
26
+ # instead if you prefer one number for all classes.
27
+ CLASS_THRESHOLDS = {0: 0.46, 1: 0.32, 2: 0.26, 3: 0.52}
28
+ CONF_FLOOR = min(CLASS_THRESHOLDS.values())
29
+
30
+
31
+ def main() -> int:
32
+ ap = argparse.ArgumentParser(description=__doc__,
33
+ formatter_class=argparse.RawDescriptionHelpFormatter)
34
+ ap.add_argument("--checkpoint", required=True, help="path to the .pth checkpoint")
35
+ ap.add_argument("--source", required=True, help="image file or folder")
36
+ ap.add_argument("--out", default=None,
37
+ help="optional folder to write YOLO-format .txt labels")
38
+ ap.add_argument("--shape", type=int, default=1024)
39
+ ap.add_argument("--global-conf", type=float, default=None,
40
+ help="use ONE threshold for all classes instead of per-class")
41
+ args = ap.parse_args()
42
+
43
+ from rfdetr import RFDETRLarge
44
+ from PIL import Image
45
+
46
+ thresholds = ({c: args.global_conf for c in CLASS_THRESHOLDS}
47
+ if args.global_conf is not None else CLASS_THRESHOLDS)
48
+ floor = min(thresholds.values())
49
+
50
+ model = RFDETRLarge.from_checkpoint(args.checkpoint)
51
+ # class_names on the checkpoint: ['none', 'header', 'text-area', 'footnote', 'footer']
52
+ names = {0: "header", 1: "text-area", 2: "footnote", 3: "footer"}
53
+
54
+ src = Path(args.source)
55
+ imgs = sorted(p for p in src.iterdir() if p.suffix.lower() in IMG_EXTS) \
56
+ if src.is_dir() else [src]
57
+
58
+ out_dir = Path(args.out) if args.out else None
59
+ if out_dir:
60
+ out_dir.mkdir(parents=True, exist_ok=True)
61
+
62
+ n_img = n_kept = 0
63
+ for ip in imgs:
64
+ n_img += 1
65
+ with Image.open(ip) as im:
66
+ W, H = im.size
67
+ det = model.predict(str(ip), threshold=floor, shape=(args.shape, args.shape))
68
+ lines = []
69
+ if det is not None and len(det) > 0:
70
+ for box, cls_id, score in zip(det.xyxy, det.class_id, det.confidence):
71
+ cls = int(cls_id) - 1 # class 0 on the checkpoint is background
72
+ if cls < 0 or cls > 3 or score < thresholds.get(cls, floor):
73
+ continue
74
+ x1, y1, x2, y2 = box.tolist()
75
+ cx, cy = ((x1 + x2) / 2) / W, ((y1 + y2) / 2) / H
76
+ w, h = (x2 - x1) / W, (y2 - y1) / H
77
+ lines.append((cls, float(score), cx, cy, w, h))
78
+ n_kept += len(lines)
79
+ print(f"{ip.stem}: {len(lines)} boxes")
80
+ for cls, score, cx, cy, w, h in lines:
81
+ print(f" {names[cls]:10} conf={score:.3f} "
82
+ f"cx={cx:.3f} cy={cy:.3f} w={w:.3f} h={h:.3f}")
83
+ if out_dir:
84
+ (out_dir / f"{ip.stem}.txt").write_text(
85
+ "".join(f"{c} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n"
86
+ for c, _, cx, cy, w, h in lines))
87
+
88
+ print(f"\n{n_img} images, {n_kept} boxes kept (thresholds: {thresholds})")
89
+ return 0
90
+
91
+
92
+ if __name__ == "__main__":
93
+ raise SystemExit(main())