File size: 4,818 Bytes
10b8d56
 
e5e756a
 
10b8d56
 
 
e5e756a
 
10b8d56
 
e5e756a
 
10b8d56
 
 
 
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10b8d56
 
 
 
 
 
e5e756a
10b8d56
 
 
 
e5e756a
10b8d56
 
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10b8d56
 
 
 
e5e756a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10b8d56
 
 
 
 
 
 
 
 
 
 
 
 
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
156
157
158
159
160
161
162
"""
OCR 引擎模块 - 封装 PaddleOCR 单例

支持 PP-OCRv6 medium det/rec 成对配置,从 HuggingFace 加载权重。
"""

import logging
import os
from typing import Optional, Dict, Any
from pathlib import Path

from app.plugins.model_probe import probe_provider, ProviderAvailability

logger = logging.getLogger(__name__)

# 全局 OCR 引擎实例
_ocr_engine: Optional[object] = None
_ocr_model_info: Optional[Dict[str, Any]] = None


# PP-OCRv6 模型配置
PP_OCRV6_MODELS = {
    "det": {
        "model_name": "PP-OCRv6_medium_det",
        "hf_model_id": "PaddlePaddle/PP-OCRv6_medium_det_safetensors",
        "provider": "ppocrv6",
    },
    "rec": {
        "model_name": "PP-OCRv6_medium_rec",
        "hf_model_id": "PaddlePaddle/PP-OCRv6_medium_rec_safetensors",
        "provider": "ppocrv6_rec",
    },
}


def get_ocr_model_config() -> Dict[str, Any]:
    """获取 OCR 模型配置

    从环境变量或默认配置读取 det/rec 模型 ID。

    Returns:
        Dict 包含 det 和 rec 的模型配置
    """
    det_model = os.getenv("OCR_DET_MODEL", PP_OCRV6_MODELS["det"]["model_name"])
    rec_model = os.getenv("OCR_REC_MODEL", PP_OCRV6_MODELS["rec"]["model_name"])

    return {
        "det": {
            "model_name": det_model,
            "hf_model_id": PP_OCRV6_MODELS["det"]["hf_model_id"],
            "provider": PP_OCRV6_MODELS["det"]["provider"],
        },
        "rec": {
            "model_name": rec_model,
            "hf_model_id": PP_OCRV6_MODELS["rec"]["hf_model_id"],
            "provider": PP_OCRV6_MODELS["rec"]["provider"],
        },
    }


def probe_ocr_providers() -> Dict[str, ProviderAvailability]:
    """探测 OCR provider 的可用性

    检查 paddleocr 和 paddle 依赖是否可用。

    Returns:
        Dict 包含 det 和 rec provider 的可用性状态
    """
    config = get_ocr_model_config()
    results = {}

    for key, cfg in config.items():
        status = probe_provider(
            provider=cfg["provider"],
            model_id=cfg["hf_model_id"],
            check_dependencies=["paddleocr", "paddle"],
        )
        results[key] = status.status

    return results


def get_ocr_engine():
    """
    获取全局 OCR 引擎实例(单例模式)

    使用 PP-OCRv6 medium det/rec 成对配置,从 HuggingFace 加载权重。

    Returns:
        PaddleOCR 实例
    """
    global _ocr_engine, _ocr_model_info

    if _ocr_engine is None:
        config = get_ocr_model_config()
        det_cfg = config["det"]
        rec_cfg = config["rec"]

        logger.info(f"正在初始化 OCR 引擎: det={det_cfg['model_name']}, rec={rec_cfg['model_name']}")

        try:
            from paddleocr import PaddleOCR

            # 使用 PP-OCRv6 medium det/rec 成对配置
            # 使用 transformers engine 避免 paddle inference 的兼容性问题
            _ocr_engine = PaddleOCR(
                text_detection_model_name=det_cfg["model_name"],
                text_recognition_model_name=rec_cfg["model_name"],
                use_doc_orientation_classify=False,  # 禁用文档方向分类
                use_doc_unwarping=False,              # 禁用文档矫正
                use_textline_orientation=False,       # 禁用文本行方向分类
                lang='ch',                            # 中文模型
                engine="transformers",                # 使用 transformers engine
            )

            # 记录模型信息
            _ocr_model_info = {
                "det_model": det_cfg["model_name"],
                "rec_model": rec_cfg["model_name"],
                "det_hf_id": det_cfg["hf_model_id"],
                "rec_hf_id": rec_cfg["hf_model_id"],
            }

            logger.info("OCR 引擎初始化完成")
        except Exception as e:
            logger.error(f"OCR 引擎初始化失败: {e}")
            raise

    return _ocr_engine


def get_ocr_model_info() -> Dict[str, Any]:
    """获取当前 OCR 模型信息

    Returns:
        Dict 包含 det_model、rec_model 等信息
    """
    global _ocr_model_info
    if _ocr_model_info is None:
        config = get_ocr_model_config()
        _ocr_model_info = {
            "det_model": config["det"]["model_name"],
            "rec_model": config["rec"]["model_name"],
            "det_hf_id": config["det"]["hf_model_id"],
            "rec_hf_id": config["rec"]["hf_model_id"],
        }
    return _ocr_model_info


def preload_ocr_engine():
    """
    预加载 OCR 引擎

    在应用启动时调用,提前加载模型以加快后续响应速度
    """
    try:
        engine = get_ocr_engine()
        logger.info("OCR 模型预加载成功")
        return engine
    except Exception as e:
        logger.error(f"OCR 模型预加载失败: {e}")
        raise