File size: 3,210 Bytes
da894bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""Run the TiBLA PP-DocLayout-L Tibetan book layout detector on one or more page
images, writing YOLO-format labels.

The model is a 4-class PP-DocLayout-L (header, text-area, footnote, footer)
fine-tuned on the leak-free v4 `tam2col` split of TiBLAD. It is served from the
exported PaddlePaddle inference model in `inference/`. The recommended global
operating confidence is ~0.68 (the best-mean-F1 point on the v4 test).

Usage:
    python infer.py --model-dir inference --source page.jpg
    python infer.py --model-dir inference --source pages/ --out preds --conf 0.68

Requires: paddlepaddle==3.0.0, paddlex.
"""
from __future__ import annotations

import argparse
from pathlib import Path

IMG_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tif", ".tiff"}
# native class order from inference.yml
NAMES = {"header": 0, "text-area": 1, "footnote": 2, "footer": 3}


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__,
                                 formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--model-dir", default="inference",
                    help="path to the exported inference model dir")
    ap.add_argument("--source", required=True, help="image file or folder")
    ap.add_argument("--out", default=None,
                    help="optional folder to write YOLO-format .txt labels")
    ap.add_argument("--conf", type=float, default=0.68,
                    help="global operating confidence (default 0.68)")
    args = ap.parse_args()

    from PIL import Image
    from paddlex import create_model

    model = create_model(model_name="PP-DocLayout-L", model_dir=args.model_dir)

    src = Path(args.source)
    imgs = sorted(p for p in src.iterdir() if p.suffix.lower() in IMG_EXTS) \
        if src.is_dir() else [src]
    out_dir = Path(args.out) if args.out else None
    if out_dir:
        out_dir.mkdir(parents=True, exist_ok=True)

    n_img = n_kept = 0
    for ip in imgs:
        n_img += 1
        with Image.open(ip) as im:
            W, H = im.size
        lines = []
        for res in model.predict(str(ip), threshold=args.conf):
            for box in res["boxes"]:
                label = box["label"]
                cls = NAMES.get(label)
                if cls is None:
                    continue
                x1, y1, x2, y2 = box["coordinate"]
                cx, cy = ((x1 + x2) / 2) / W, ((y1 + y2) / 2) / H
                w, h = (x2 - x1) / W, (y2 - y1) / H
                lines.append((cls, float(box["score"]), cx, cy, w, h))
        n_kept += len(lines)
        print(f"{ip.stem}: {len(lines)} boxes")
        for cls, score, cx, cy, w, h in lines:
            name = next(k for k, v in NAMES.items() if v == cls)
            print(f"    {name:10} conf={score:.3f}  cx={cx:.3f} cy={cy:.3f} "
                  f"w={w:.3f} h={h:.3f}")
        if out_dir:
            (out_dir / f"{ip.stem}.txt").write_text(
                "".join(f"{c} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n"
                        for c, _, cx, cy, w, h in lines))

    print(f"\n{n_img} images, {n_kept} boxes kept (conf {args.conf})")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())