mirror of
https://github.com/RapidAI/RapidOCR.git
synced 2026-11-10 13:03:58 +08:00
feat: validate python-bidi before initializing Arabic OCR (issue #719)
This commit is contained in:
4 files changed
+93
-11
No files matched your search
@@ -42,6 +42,9 @@ Changelog = "https://github.com/RapidAI/RapidOCR/releases"
|
||||
[project.scripts]
|
||||
rapidocr = "rapidocr.main:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
rtl = ["python-bidi>=0.4.2,<1.0"]
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = true
|
||||
platforms = ["Any"]
|
||||
|
||||
@@ -24,7 +24,8 @@ from rapidocr.inference_engine.base import FileInfo, get_engine
|
||||
from ..utils.download_file import DownloadFile, DownloadFileInput
|
||||
from ..utils.log import logger
|
||||
from ..utils.model_resolver import normalize_lang
|
||||
from ..utils.utils import reorder_bidi_for_display
|
||||
from ..utils.typings import LangRec
|
||||
from ..utils.utils import reorder_bidi_for_display, validate_rtl_dependency
|
||||
from ..utils.vis_res import VisRes
|
||||
from .typings import TextRecInput, TextRecOutput
|
||||
from .utils import CTCLabelDecode
|
||||
@@ -32,10 +33,14 @@ from .utils import CTCLabelDecode
|
||||
DEFAULT_DICT_PATH = Path(__file__).parent.parent / "models" / "ppocr_keys_v1.txt"
|
||||
DEFAULT_DICT_URL = "https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v2.0.7/paddle/PP-OCRv4/rec/ch_PP-OCRv4_rec_infer/ppocr_keys_v1.txt"
|
||||
DEFAULT_MODEL_PATH = Path(__file__).parent.parent / "models"
|
||||
RTL_LANGS = {LangRec.ARABIC.value}
|
||||
|
||||
|
||||
class TextRecognizer:
|
||||
def __init__(self, cfg: Dict[str, Any]):
|
||||
if normalize_lang(cfg.lang_type) in RTL_LANGS:
|
||||
validate_rtl_dependency()
|
||||
|
||||
self.session = get_engine(cfg.engine_type)(cfg)
|
||||
|
||||
# onnx has inner character, other engine get or download character_dict_path
|
||||
@@ -50,8 +55,6 @@ class TextRecognizer:
|
||||
|
||||
self.cfg = cfg
|
||||
|
||||
self.RTL_LANGS = {"arabic", "fa", "ur"}
|
||||
|
||||
def get_character_dict(self, cfg):
|
||||
character = None
|
||||
dict_path = cfg.get("rec_keys_path", None)
|
||||
@@ -139,7 +142,7 @@ class TextRecognizer:
|
||||
all_line_results, all_word_results = list(zip(*rec_res))
|
||||
txts, scores = list(zip(*all_line_results))
|
||||
|
||||
if normalize_lang(self.cfg.lang_type) in self.RTL_LANGS:
|
||||
if normalize_lang(self.cfg.lang_type) in RTL_LANGS:
|
||||
txts = reorder_bidi_for_display(txts)
|
||||
|
||||
elapse = time.perf_counter() - start_time
|
||||
|
||||
@@ -12,16 +12,22 @@ import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
def validate_rtl_dependency():
|
||||
try:
|
||||
from bidi.algorithm import get_display # noqa: F401
|
||||
except ImportError as exc:
|
||||
raise ModuleNotFoundError(
|
||||
"RTL recognition requires the optional dependency 'python-bidi'. "
|
||||
"Install it with: pip install 'rapidocr[rtl]'"
|
||||
) from exc
|
||||
|
||||
|
||||
def reorder_bidi_for_display(
|
||||
texts: Tuple[Union[str, bytes], ...],
|
||||
) -> Tuple[Union[str, bytes], ...]:
|
||||
try:
|
||||
from bidi.algorithm import get_display
|
||||
except ImportError as e:
|
||||
raise ModuleNotFoundError(
|
||||
"Required dependency 'python-bidi' is not installed. "
|
||||
"Install it with: pip install python-bidi"
|
||||
) from e
|
||||
validate_rtl_dependency()
|
||||
|
||||
from bidi.algorithm import get_display # noqa: F401
|
||||
|
||||
return tuple([get_display(text) for text in texts])
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# @Author: SWHL
|
||||
# @Contact: liekkaskono@163.com
|
||||
import builtins
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
root_dir = Path(__file__).resolve().parent.parent
|
||||
sys.path.append(str(root_dir))
|
||||
|
||||
from rapidocr.ch_ppocr_rec import main as rec_main
|
||||
from rapidocr.utils import utils
|
||||
from rapidocr.utils.model_resolver import normalize_lang
|
||||
from rapidocr.utils.typings import EngineType, LangRec
|
||||
|
||||
|
||||
def test_validate_rtl_dependency_reports_extra(monkeypatch):
|
||||
original_import = builtins.__import__
|
||||
|
||||
def missing_bidi(name, *args, **kwargs):
|
||||
if name.startswith("bidi"):
|
||||
raise ModuleNotFoundError("No module named 'bidi'")
|
||||
return original_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", missing_bidi)
|
||||
|
||||
with pytest.raises(ModuleNotFoundError, match=r"rapidocr\[rtl\]"):
|
||||
utils.validate_rtl_dependency()
|
||||
|
||||
|
||||
def test_reorder_bidi_for_display_uses_bidi_algorithm(monkeypatch):
|
||||
bidi_module = types.ModuleType("bidi")
|
||||
algorithm_module = types.ModuleType("bidi.algorithm")
|
||||
algorithm_module.get_display = lambda text: text[::-1]
|
||||
bidi_module.algorithm = algorithm_module
|
||||
monkeypatch.setitem(sys.modules, "bidi", bidi_module)
|
||||
monkeypatch.setitem(sys.modules, "bidi.algorithm", algorithm_module)
|
||||
|
||||
assert utils.reorder_bidi_for_display(("abc", "123")) == ("cba", "321")
|
||||
|
||||
|
||||
def test_arabic_dependency_is_checked_before_engine_initialization(monkeypatch):
|
||||
engine_called = False
|
||||
|
||||
def fail_get_engine(_engine_type):
|
||||
nonlocal engine_called
|
||||
engine_called = True
|
||||
raise AssertionError(
|
||||
"engine initialization must happen after dependency validation"
|
||||
)
|
||||
|
||||
def missing_dependency():
|
||||
raise ModuleNotFoundError("RTL dependency is missing")
|
||||
|
||||
monkeypatch.setattr(rec_main, "get_engine", fail_get_engine)
|
||||
monkeypatch.setattr(rec_main, "validate_rtl_dependency", missing_dependency)
|
||||
|
||||
cfg = SimpleNamespace(lang_type=LangRec.ARABIC, engine_type=EngineType.ONNXRUNTIME)
|
||||
with pytest.raises(ModuleNotFoundError, match="RTL dependency is missing"):
|
||||
rec_main.TextRecognizer(cfg)
|
||||
|
||||
assert engine_called is False
|
||||
|
||||
|
||||
def test_arabic_enum_is_normalized_for_rtl_detection():
|
||||
assert normalize_lang(LangRec.ARABIC) in rec_main.RTL_LANGS
|
||||
Reference in new issue
Block a user