RapidOCR 本地部署

RapidOCR 本地部署

2026-07-02 10:50

dockerfile

# 使用精简的 Python 基础镜像(替代 ~2GB 的 PaddlePaddle 镜像)
FROM python:3.10-slim
# 设置工作目录
WORKDIR /app
# 安装运行时系统依赖(OpenCV/PIL 所需的共享库)
# 不再需要 gcc/g++/python3-dev,因为没有 PaddlePaddle/lmdb 编译需求
# 注: Debian Trixie 已移除 libgl1-mesa-glx 过渡包,改用 libgl1
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        libgl1 \
        libglib2.0-0 \
        libgomp1 \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*
# 升级 pip
RUN pip install --no-cache-dir --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装 Python 依赖
# RapidOCR + ONNXRuntime 替代 PaddleOCR + PaddlePaddle,镜像体积从 ~3GB 降到 ~800MB
RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple \
    rapidocr-onnxruntime \
    fastapi \
    uvicorn[standard] \
    python-multipart \
    Pillow \
    numpy
# 复制应用程序代码
COPY . /app
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

main.py

# main.py
from fastapi import FastAPI, UploadFile, File, HTTPException
from rapidocr_onnxruntime import RapidOCR
import numpy as np
from PIL import Image
import io
import asyncio
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# --- 1. 轻量化初始化 RapidOCR ---
# RapidOCR 默认使用 PP-OCRv4 mobile 模型 + ONNXRuntime 后端
# 相比 PaddleOCR 省去整个 PaddlePaddle 框架,内存占用降低约 60%-70%
ocr = RapidOCR()
logger.info("RapidOCR 初始化完成")
# --- 2. FastAPI 应用实例 ---
app = FastAPI(title="RapidOCR Service")
# --- 3. 辅助函数(将同步OCR操作放入线程池) ---
def run_ocr(image_np):
    """同步 OCR 调用,放在线程池中执行"""
    return ocr(image_np)
# --- 4. API 路由 ---
@app.post("/ocr")
async def perform_ocr(file: UploadFile = File(...)):
    # 1. 读取文件内容
    contents = await file.read()
    # 2. 限制上传文件大小(例如 2MB)
    if len(contents) > 2 * 1024 * 1024:
        raise HTTPException(status_code=413, detail="图片大小不能超过 2MB")
    try:
        # 3. 转换为 PIL Image 并压缩(限制长边 800px)
        image = Image.open(io.BytesIO(contents)).convert('RGB')
        max_size = 800
        # 保持宽高比进行缩放
        image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
        image_np = np.array(image)
        logger.info(f"开始识别,图片尺寸: {image_np.shape}")
        # 4. 使用 run_in_executor 将同步任务放到线程池,并设置 30 秒超时
        loop = asyncio.get_event_loop()
        result, elapse = await asyncio.wait_for(
            loop.run_in_executor(None, run_ocr, image_np),
            timeout=30.0
        )
        # 5. 提取文字
        # RapidOCR 返回格式: result = [[box, text, score], ...] 或 None
        texts = []
        logger.info(f"OCR 原始返回结果: {result}, 耗时: {elapse}")
        if result:
            for line in result:
                logger.info(f"单行数据类型: {type(line)}, 内容: {line}")
                try:
                    # line 结构: [box, text, score]
                    if line is not None and len(line) >= 2 and line[1] is not None:
                        texts.append(line[1])
                except (TypeError, IndexError) as e:
                    logger.warning(f"跳过异常行数据: {line}, 错误: {e}")
        logger.info(f"识别成功,文字数: {len(texts)}")
        return {"texts": texts}
    except asyncio.TimeoutError:
        logger.error("OCR 识别超时(30秒)")
        raise HTTPException(status_code=504, detail="OCR 处理超时")
    except Exception as e:
        logger.error(f"OCR 识别出错: {str(e)}")
        raise HTTPException(status_code=500, detail=f"OCR 识别失败: {str(e)}")

放在同一个目录,然后执行

# 1. 重新构建镜像(建议换 tag 避免和旧镜像混淆)
docker build -t my-rapidocr .
# 2. 运行(500m 内存限制可以稳跑了,但建议给到 800m 更稳妥)
docker run --rm -p 8000:8000 \
  -v $(pwd):/app \
  --memory="800m" \
  --memory-swap="1g" \
  --name ocr_api \
  my-rapidocr

测试

curl -X POST "http://localhost:8000/ocr" \
  -F "file=@test.png"