将 Tesseract 作为 API 运行
或作为自托管服务。
放心地将 Tesseract 封装为 HTTP 服务:生产级 FastAPI 端点、进程池并发、包含语言包和健康检查的 Docker 镜像、扩缩容与背压,以及无服务器部署——并对比托管 OCR API 的隐私与成本权衡。
TL;DR — 最小服务
将 CLI 或 pytesseract 绑定封装在 HTTP 之后。图像永远不会离开你的基础设施。
pip install fastapi uvicorn python-multipart pytesseract pillow
# 先安装引擎 + 语言包(参见安装指南)
uvicorn app:app --host 0.0.0.0 --port 8000然后调用:curl -F "file=@invoice.png" "http://localhost:8000/ocr?lang=chi_sim"。Tesseract 本身按安装指南安装。
先做设计决策
三个选择决定整个服务。
| 决策 | 选项 | 建议 |
|---|---|---|
| 同步 vs 异步 | 阻塞请求 vs 任务队列 | 单张图像几秒内用同步;大文档或批处理用异步任务 ID。 |
| 输入 | multipart 上传 vs base64 JSON | 二进制图像优先 multipart;仅当客户端无法上传文件时才用 base64(增加约 33% 体积)。 |
| 输出 | 纯文本、JSON、hOCR/ALTO、可搜索 PDF | 默认返回 JSON;为版式提供 hOCR,为归档提供 PDF 下载 URL。 |
每个端点一个主要意图。/ocr 保持返回文本,为 PDF 和版式数据另设端点,而不是让一个响应过载。
生产级 Python 服务(FastAPI)
校验、大小限制、超时,以及让 OCR 离开事件循环。
import asyncio, io
from concurrent.futures import ProcessPoolExecutor
from fastapi import FastAPI, File, UploadFile, HTTPException, Query
from PIL import Image
import pytesseract
app = FastAPI(title="Tesseract OCR API")
pool = ProcessPoolExecutor(max_workers=4)
MAX_BYTES = 10 * 1024 * 1024 # 10 MB
LANGS = {"eng", "chi_sim"}
def _ocr(data: bytes, lang: str) -> str:
image = Image.open(io.BytesIO(data))
return pytesseract.image_to_string(image, lang=lang)
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/ocr")
async def ocr(file: UploadFile = File(...), lang: str = Query("chi_sim")):
if lang not in LANGS:
raise HTTPException(400, "不支持的语言")
data = await file.read()
if len(data) > MAX_BYTES:
raise HTTPException(413, "文件过大")
loop = asyncio.get_running_loop()
try:
text = await asyncio.wait_for(loop.run_in_executor(pool, _ocr, data, lang), timeout=30)
except asyncio.TimeoutError:
raise HTTPException(504, "OCR 超时")
return {"text": text, "lang": lang}关键点:语言白名单,在解码之前强制字节上限,在进程池中运行 OCR,并限制实际耗时。完整的 pytesseract API 见 Python 指南。
容器化
将 Tesseract 和语言包烘焙进镜像,使副本完全一致。
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr tesseract-ocr-eng tesseract-ocr-chi-sim \
libgl1 libglib2.0-0 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
HEALTHCHECK CMD python -c "import urllib.request;urllib.request.urlopen('http://localhost:8000/health')"
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]只添加需要的语言包以保持镜像精简。安装指南更详细地介绍了 Docker 和 tessdata 的放置。
并发与扩缩容
OCR 是 CPU 密集型——用进程而非线程并行。
- 进程池或 CLI 子进程: Python 的 GIL 使线程池无法加速 OCR。使用
ProcessPoolExecutor或tesseract子进程池。 - 工作进程 vs 副本: 每个容器运行几个 uvicorn 工作进程,并在负载均衡器后水平扩展副本。
- 队列: 面对突发或长文档,接受上传、返回任务 ID,并从队列(Celery、RQ 或消息代理)处理。
- 背压: 为队列设上限,饱和时返回
429拒绝,而不是让延迟失控。 - 资源限制: 为每个工作进程固定 CPU/内存,并设置 OCR 超时,使一张坏图不会拖垮副本。
安全与隐私
自托管是团队选择 Tesseract 的主要原因。
- 数据不外流: 图像留在你的网络内——对法律、医疗和金融文档很重要。
- 校验输入: 检查 MIME 类型和魔数字节,而不仅是扩展名;在 try/except 中解码。
- 限制资源: 最大上传大小、每请求超时和内存上限,以抵御解压炸弹。
- 鉴权: 在前面放置 API 密钥或网关;OCR 端点被滥用代价高昂。
- 清除元数据: 若持久化文件,移除 EXIF 并定期删除原件。
无服务器与容器平台
- 容器运行时: Cloud Run、AWS Lambda 容器镜像和 Fly.io 都能在无需管理服务器的情况下运行 Tesseract 镜像。
- 冷启动: 含语言包的 Tesseract 镜像很大,冷启动较慢——为延迟敏感流量保持一个热实例。
- 限制: 提高内存和超时;默认的小超时会杀掉大文档。
- 临时磁盘: 临时文件只写入
/tmp;将文件系统视为一次性。
自托管 Tesseract vs 托管 OCR API
| 维度 | 自托管 Tesseract | 托管 OCR API |
|---|---|---|
| 成本 | 固定基础设施;无按页费用 | 按请求/页付费;随用量增长 |
| 隐私 | 文档永不离开你的网络 | 文档发送给第三方 |
| 精度 | 清晰印刷体表现强;可按领域调优 | 在困难扫描件和手写上往往更强 |
| 控制 | 完全:版本、语言、自定义模型 | 供应商路线图与配额 |
| 运维 | 你负责:扩缩容、升级、监控 | 完全托管 |
许多团队对大批量、隐私敏感的工作自托管 Tesseract,只对少数困难文档回退到托管 API。
API 常见问题
如何将 Tesseract OCR 作为 API 运行?
将 CLI 或 pytesseract 封装在 FastAPI/Flask 中,暴露 POST /ocr 并容器化。图像留在你的基础设施内。
用线程还是进程?
进程。OCR 是 CPU 密集型,GIL 阻碍线程并行;使用 ProcessPoolExecutor 或 CLI 子进程。
自托管 API 是否私密?
是:上传的图像在本地处理,绝不发送给第三方,不同于托管 OCR API。
能在无服务器上运行吗?
可以,在 Cloud Run、Lambda 容器镜像或 Fly.io 等容器平台上;注意冷启动和含语言包的镜像体积。
可以返回哪些格式?
纯文本、带置信度的 JSON、hOCR/ALTO,或通过下载 URL 提供的可搜索 PDF。