RapidOCR 本地部署
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"