| |
| """Portable logical-order Persian line recognition for the Bina 0.2 family.""" |
| from __future__ import annotations |
| import argparse |
| import json |
| import re |
| from pathlib import Path |
| from typing import Any, Iterator |
| from paddleocr import TextRecognition |
|
|
| _LTR_RUN = re.compile(r"[a-zA-Z0-9 :*./%+-]") |
|
|
| def pred_reverse(text: str) -> str: |
| """Convert PaddleOCR visual-order Arabic output to logical reading order.""" |
| segments: list[str] = [] |
| current_ltr = "" |
| for character in text: |
| if _LTR_RUN.search(character): |
| current_ltr += character |
| continue |
| if current_ltr: |
| segments.append(current_ltr) |
| current_ltr = "" |
| segments.append(character) |
| if current_ltr: |
| segments.append(current_ltr) |
| return "".join(reversed(segments)) |
|
|
| class BinaTextRecognition: |
| def __init__(self, model_dir: str | Path | None = None, device: str | None = None) -> None: |
| model_dir = Path(model_dir) if model_dir else Path(__file__).resolve().parent / "inference" |
| options: dict[str, Any] = {"model_dir": str(model_dir)} |
| if device: |
| options["device"] = device |
| self._model = TextRecognition(**options) |
|
|
| def predict(self, inputs: str | Path | list[str] | list[Path], batch_size: int = 1) -> Iterator[dict[str, Any]]: |
| for result in self._model.predict(input=inputs, batch_size=batch_size): |
| payload = result.json() if callable(result.json) else result.json |
| raw = payload["res"] |
| visual = str(raw["rec_text"]) |
| yield { |
| "input_path": raw.get("input_path"), |
| "text": pred_reverse(visual), |
| "score": float(raw["rec_score"]), |
| "raw_visual_text": visual, |
| } |
|
|
| def main() -> int: |
| parser = argparse.ArgumentParser(description="Recognize Persian text-line crops with Bina 0.2") |
| parser.add_argument("images", nargs="+") |
| parser.add_argument("--model-dir", default=str(Path(__file__).resolve().parent / "inference")) |
| parser.add_argument("--device", default="cpu", help="cpu, gpu:0, ...") |
| parser.add_argument("--batch-size", type=int, default=1) |
| args = parser.parse_args() |
| model = BinaTextRecognition(args.model_dir, device=args.device) |
| for prediction in model.predict(args.images, batch_size=args.batch_size): |
| print(json.dumps(prediction, ensure_ascii=False)) |
| return 0 |
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|