handsme commited on
Commit
e5564f3
·
1 Parent(s): 689f815

重构:PaddlePaddle → RapidOCR + PP-OCRv5 ONNX(轻量快速)

Browse files
Files changed (3) hide show
  1. Dockerfile +16 -34
  2. app.py +27 -38
  3. requirements.txt +3 -3
Dockerfile CHANGED
@@ -1,54 +1,36 @@
1
- # 照搬 piika919/paddle-ui 的多阶段构建方式
2
- FROM python:3.10-slim as builder
3
 
4
- # 安装构建依赖
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
6
- build-essential \
7
  libgl1 \
8
  libglib2.0-0 \
9
- libsm6 \
10
- libxext6 \
11
- libxrender-dev \
12
  libgomp1 \
 
13
  && rm -rf /var/lib/apt/lists/*
14
 
15
- # 创建虚拟环境
16
- RUN python -m venv /opt/venv
17
- ENV PATH="/opt/venv/bin:$PATH"
18
 
19
- # 安装依赖
20
  COPY requirements.txt .
21
  RUN pip install --no-cache-dir --upgrade pip && \
22
  pip install --no-cache-dir -r requirements.txt
23
 
24
- # 最终镜像
25
- FROM python:3.10-slim
26
-
27
- # 运行时依赖
28
- RUN apt-get update && apt-get install -y --no-install-recommends \
29
- libgl1 \
30
- libglib2.0-0 \
31
- libsm6 \
32
- libxext6 \
33
- libxrender-dev \
34
- libgomp1 \
35
- curl \
36
- && rm -rf /var/lib/apt/lists/*
37
-
38
- # 复制虚拟环境
39
- COPY --from=builder /opt/venv /opt/venv
40
- ENV PATH="/opt/venv/bin:$PATH"
41
-
42
- WORKDIR /app
43
  COPY app.py .
44
 
45
- # 预下载 PP-OCRv5 模型
46
- ENV DISABLE_MODEL_SOURCE_CHECK=True
47
- RUN python -c "from paddleocr import PaddleOCR; PaddleOCR(lang='en', ocr_version='PP-OCRv5', device='cpu')"
 
 
 
 
 
48
 
49
  EXPOSE 7860
50
 
51
- HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
52
  CMD curl -f http://localhost:7860/ || exit 1
53
 
54
  CMD ["python", "app.py"]
 
1
+ # PP-OCRv5 ONNX 轻量部署(RapidOCR + ONNX Runtime)
2
+ FROM python:3.10-slim
3
 
4
+ # 安装系统依赖
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
 
6
  libgl1 \
7
  libglib2.0-0 \
 
 
 
8
  libgomp1 \
9
+ curl \
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
+ WORKDIR /app
 
 
13
 
14
+ # 安装 Python 依赖
15
  COPY requirements.txt .
16
  RUN pip install --no-cache-dir --upgrade pip && \
17
  pip install --no-cache-dir -r requirements.txt
18
 
19
+ # 复制代码
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  COPY app.py .
21
 
22
+ # 预下载 PP-OCRv5 ONNX 模型(避免首次请求慢)
23
+ RUN python -c "
24
+ from huggingface_hub import hf_hub_download
25
+ hf_hub_download('monkt/paddleocr-onnx', 'detection/v5/det.onnx')
26
+ hf_hub_download('monkt/paddleocr-onnx', 'languages/english/rec.onnx')
27
+ hf_hub_download('monkt/paddleocr-onnx', 'languages/english/dict.txt')
28
+ print('PP-OCRv5 ONNX models downloaded successfully')
29
+ "
30
 
31
  EXPOSE 7860
32
 
33
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
34
  CMD curl -f http://localhost:7860/ || exit 1
35
 
36
  CMD ["python", "app.py"]
app.py CHANGED
@@ -4,19 +4,22 @@ import base64
4
  import io
5
  import numpy as np
6
  from PIL import Image
7
- from paddleocr import PaddleOCR
 
8
  from fastapi import FastAPI, Request
9
  from fastapi.responses import RedirectResponse
10
  import uvicorn
11
 
12
- # 照搬 piika919/paddle-ui 的初始化方式
13
- ocr_engine = PaddleOCR(
14
- lang="en",
15
- ocr_version="PP-OCRv5",
16
- use_doc_orientation_classify=False,
17
- use_doc_unwarping=False,
18
- use_textline_orientation=False,
19
- device="cpu",
 
 
20
  )
21
 
22
 
@@ -30,39 +33,24 @@ def ocr_recognize(image):
30
  else:
31
  img_array = image
32
 
33
- # PP-OCRv5 使 .predict()
34
- results = ocr_engine.predict(img_array)
35
 
36
  lines = []
37
  raw_results = []
38
 
39
- for res in results:
40
- # 照搬 piika919 的结果解析方式,兼容 dict 和 object
41
- def get_val(obj, key, default=None):
42
- if isinstance(obj, dict) or hasattr(obj, 'keys'):
43
- return obj.get(key, default)
44
- return getattr(obj, key, default)
45
-
46
- rec_polys = get_val(res, "rec_polys")
47
- rec_texts = get_val(res, "rec_texts")
48
- rec_scores = get_val(res, "rec_scores")
49
-
50
- if rec_texts is None:
51
- continue
52
-
53
- scores = rec_scores if rec_scores is not None else [1.0] * len(rec_texts)
54
- polys = rec_polys if rec_polys is not None else [None] * len(rec_texts)
55
 
56
- for i, (text, score) in enumerate(zip(rec_texts, scores)):
57
  lines.append(text)
58
- bbox = []
59
- if i < len(polys) and polys[i] is not None:
60
- poly = polys[i]
61
- bbox = poly.tolist() if isinstance(poly, np.ndarray) else poly
62
  raw_results.append({
63
  "text": text,
64
- "confidence": round(float(score), 4),
65
- "bbox": bbox,
66
  })
67
 
68
  return json.dumps({
@@ -70,6 +58,7 @@ def ocr_recognize(image):
70
  "lines": lines,
71
  "full_text": "\n".join(lines),
72
  "raw": raw_results,
 
73
  }, ensure_ascii=False, indent=2)
74
  except Exception as e:
75
  return json.dumps({"success": False, "error": str(e)}, ensure_ascii=False)
@@ -89,7 +78,7 @@ async def api_predict(request: Request):
89
  image_bytes = base64.b64decode(base64_str)
90
  image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
91
 
92
- # 限制图片最大边为 2000px,防止大图导致 PaddlePaddle C++ 层内存溢出
93
  max_side = 2000
94
  w, h = image.size
95
  if max(w, h) > max_side:
@@ -103,8 +92,8 @@ async def api_predict(request: Request):
103
 
104
 
105
  # ---- Gradio 界面 ----
106
- with gr.Blocks(title="n1payocr API - PP-OCRv5", analytics_enabled=False) as demo:
107
- gr.Markdown("# 🔍 n1payocr 文字识别引擎 (PP-OCRv5)")
108
  with gr.Row():
109
  with gr.Column():
110
  input_image = gr.Image(type="pil", label="上传图片")
@@ -113,7 +102,7 @@ with gr.Blocks(title="n1payocr API - PP-OCRv5", analytics_enabled=False) as demo
113
  output_text = gr.Textbox(label="识别结果 (JSON)", lines=20)
114
  submit_btn.click(fn=ocr_recognize, inputs=input_image, outputs=output_text, api_name=False)
115
 
116
- # 挂载 Gradio 到 /ui 路径,show_api=False 隐藏 API 文档
117
  app = gr.mount_gradio_app(app, demo, path="/ui")
118
 
119
 
 
4
  import io
5
  import numpy as np
6
  from PIL import Image
7
+ from huggingface_hub import hf_hub_download
8
+ from rapidocr_onnxruntime import RapidOCR
9
  from fastapi import FastAPI, Request
10
  from fastapi.responses import RedirectResponse
11
  import uvicorn
12
 
13
+ # 下载 PP-OCRv5 ONNX 模型
14
+ det_path = hf_hub_download("monkt/paddleocr-onnx", "detection/v5/det.onnx")
15
+ rec_path = hf_hub_download("monkt/paddleocr-onnx", "languages/english/rec.onnx")
16
+ dict_path = hf_hub_download("monkt/paddleocr-onnx", "languages/english/dict.txt")
17
+
18
+ # 初始化 RapidOCR(PP-OCRv5 ONNX 推理)
19
+ ocr_engine = RapidOCR(
20
+ det_model_path=det_path,
21
+ rec_model_path=rec_path,
22
+ rec_keys_path=dict_path,
23
  )
24
 
25
 
 
33
  else:
34
  img_array = image
35
 
36
+ # RapidOCR
37
+ result, elapsed = ocr_engine(img_array)
38
 
39
  lines = []
40
  raw_results = []
41
 
42
+ if result:
43
+ for item in result:
44
+ # RapidOCR 返回格式: (bbox, (text, confidence))
45
+ bbox = item[0] # [[x1,y1],[x2,y2],[x3,y3],[x4,y4]]
46
+ text = item[1][0]
47
+ confidence = item[1][1]
 
 
 
 
 
 
 
 
 
 
48
 
 
49
  lines.append(text)
 
 
 
 
50
  raw_results.append({
51
  "text": text,
52
+ "confidence": round(float(confidence), 4),
53
+ "bbox": bbox if isinstance(bbox, list) else bbox.tolist(),
54
  })
55
 
56
  return json.dumps({
 
58
  "lines": lines,
59
  "full_text": "\n".join(lines),
60
  "raw": raw_results,
61
+ "elapsed": round(elapsed, 3),
62
  }, ensure_ascii=False, indent=2)
63
  except Exception as e:
64
  return json.dumps({"success": False, "error": str(e)}, ensure_ascii=False)
 
78
  image_bytes = base64.b64decode(base64_str)
79
  image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
80
 
81
+ # 限制图片最大边为 2000px,防止大图导致内存溢出
82
  max_side = 2000
83
  w, h = image.size
84
  if max(w, h) > max_side:
 
92
 
93
 
94
  # ---- Gradio 界面 ----
95
+ with gr.Blocks(title="n1payocr API - PP-OCRv5 ONNX", analytics_enabled=False) as demo:
96
+ gr.Markdown("# 🔍 n1payocr 文字识别引擎 (PP-OCRv5 ONNX)")
97
  with gr.Row():
98
  with gr.Column():
99
  input_image = gr.Image(type="pil", label="上传图片")
 
102
  output_text = gr.Textbox(label="识别结果 (JSON)", lines=20)
103
  submit_btn.click(fn=ocr_recognize, inputs=input_image, outputs=output_text, api_name=False)
104
 
105
+ # 挂载 Gradio 到 /ui 路径
106
  app = gr.mount_gradio_app(app, demo, path="/ui")
107
 
108
 
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
- # 照搬 piika919/paddle-ui 的依赖版本
2
- paddlepaddle==3.0.0
3
- paddleocr[all]>=2.9.0
4
  Pillow>=10.0.0
5
  numpy>=1.24.0
6
  opencv-python-headless>=4.8.0
 
1
+ # RapidOCR + ONNX Runtime(替代 PaddlePaddle,轻量快速)
2
+ rapidocr-onnxruntime>=1.4.0
3
+ huggingface_hub
4
  Pillow>=10.0.0
5
  numpy>=1.24.0
6
  opencv-python-headless>=4.8.0