中文 · English
基于 Java SpringBoot + ONNX Runtime 的 PP-OCRv6 OCR 识别服务,提供文本检测、文本识别和完整流水线的 REST API。
本项目使用 PaddleOCR 团队发布的 PP-OCRv6_medium 系列 ONNX 模型,在 Java 生态中实现完整的 OCR 能力。核心技术路线严格对齐 PaddleOCR Python 参考实现,确保预处理(图像归一化、尺寸缩放、通道顺序)和后处理(CTC 解码、DBPostProcess)与官方版本一致。
| 模型 | 参数量 | 用途 | 来源 |
|---|---|---|---|
| PP-OCRv6_medium_rec | 19M | 文本识别 | ModelScope |
| PP-OCRv6_medium_det | 15.5M | 文本检测 | ModelScope |
- 文本检测 (Text Detection) — DB(Differentiable Binarization)算法检测图像中文本区域,返回四边形边界框坐标
- Moore-Neighbor 轮廓追踪
- 凸包 + 旋转卡壳最小外接矩形
- Unclip 多边形扩展
- 阅读顺序排序(自上而下,从左到右)
- 文本识别 (Text Recognition) — CTC 解码,支持 50 种语言,18708 字符字典
- 双 Softmax 头(CTC + NRTR Attention)
- 空白符去重(Blank Index = 0)
- 模型索引偏移(Index 1 → charDict[0])
- 完整流水线 — 检测 → 裁剪 → 识别
- 透射变换(DLT Homography)校正文本行角度
- 自动旋转竖向文本(宽高比 ≥ 1.5)
- 双线性插值图像缩放
- Markdown 文档输出 — 布局分析 + Markdown 格式化
- 多栏检测(XY-Cut 投影分析)
- 段落合并(基于垂直间距和缩进)
- 标题级别检测(基于字体高度比)
- 列表格式自动识别
- 5 个 REST API 端点
- 版面分析增强 — 支持表格识别、页眉/页脚/页码检测、图片标题识别
- 公式识别 — 集成 LaTeX 公式 OCR 模型
- 文档方向分类 — 自动检测并纠正文档旋转角度
- 文档扭曲校正 — 对弯曲/折叠文档进行展平校正
- 流式输出 — 大文档分块处理,SSE 流式返回结果
- 批量处理 — 支持多图片并发提交
- 结果缓存 — 相同图片的 MD5 缓存,避免重复处理
- 模型预热 — 启动时预跑一次推理,避免首次请求延迟
- 性能优化 — 检测/识别模型异步并行执行(当前检测为串行瓶颈)
- 更多输出格式 — 支持 HTML、LaTeX、纯文本等输出格式
- 可配置参数 — 检测阈值、识别 batch size 等通过 yaml 配置
- Docker 部署 — 提供 Dockerfile 和 docker-compose.yml
graph TB
subgraph Controller["API 层 (OcrController)"]
RC["POST /recognize<br/>文本识别"]
DT["POST /detect<br/>文本检测"]
PL["POST /pipeline<br/>完整流水线"]
MD["POST /markdown<br/>Markdown 输出"]
end
subgraph Service["服务层"]
OcrSvc["OcrService<br/>文本识别服务"]
DetSvc["DetService<br/>文本检测服务"]
PipeSvc["OcrPipelineService<br/>流水线编排"]
end
subgraph Preprocess["预处理"]
RecPrep["ImagePreprocessor<br/>resize_norm_img<br/>HWC→CHW → 归一化 → pad"]
DetPrep["DetPreprocessor<br/>DetResizeForTest<br/>NormalizeImage → ToCHWImage"]
end
subgraph Postprocess["后处理"]
DetPost["DetPostProcessor<br/>DBPostProcess<br/>阈值化→轮廓→minAreaRect→unclip"]
CTCDecode["CTC Decode<br/>Argmax→去重→去空白→字典映射"]
end
subgraph Layout["布局分析"]
LA["LayoutAnalyzer<br/>XY-Cut 分栏 → 段落合并<br/>→ 标题级别检测"]
MC["MarkdownConverter<br/>Markdown 格式化输出"]
end
subgraph Models["ONNX 模型"]
RecModel["inference.onnx<br/>PP-OCRv6_medium_rec<br/>19M params"]
DetModel["inference_det.onnx<br/>PP-OCRv6_medium_det<br/>15.5M params"]
end
RC --> OcrSvc
DT --> DetSvc
PL --> PipeSvc
MD --> PipeSvc
OcrSvc --> RecPrep --> RecModel --> CTCDecode
DetSvc --> DetPrep --> DetModel --> DetPost
PipeSvc --> DetSvc
PipeSvc --> OcrSvc
PipeSvc --> LA --> MC
flowchart TB
IMG["📷 JPEG / PNG"]
subgraph DET["① 文本检测"]
RESIZE["DetResizeForTest<br/>缩放至 736 边,对齐 32 的倍数"]
NORM["NormalizeImage<br/>÷255 → (x - μ) / σ"]
INFER_D["ONNX 推理 (det)"]
DBPOST["DBPostProcess<br/>阈值 0.3 → 轮廓追踪<br/>→ minAreaRect → unclip"]
end
SORT["② 阅读顺序排序<br/>按 Y 分组 → 行内 X 排序"]
CROP["③ 透视变换裁剪<br/>DLT Homography → 双线性采样"]
subgraph REC["④ 文本识别"]
RCPREP["resize_norm_img<br/>高 48,宽等比 ≤ 320"]
INFER_R["ONNX 推理 (rec)"]
CTC["CTC 解码<br/>blank=0 → argmax → 去重"]
end
subgraph LAYOUT["⑤ 布局分析 + Markdown"]
XY["XY-Cut 分栏检测"]
MERGE["段落合并"]
HEADING["标题级别检测"]
MD_OUT["Markdown 格式化"]
end
OUTPUT["📄 Markdown 文档"]
IMG --> DET
DET --> RESIZE --> NORM --> INFER_D --> DBPOST
DBPOST --> SORT
SORT --> CROP
CROP --> REC
REC --> RCPREP --> INFER_R --> CTC
CTC --> LAYOUT
LAYOUT --> XY --> MERGE --> HEADING --> MD_OUT
MD_OUT --> OUTPUT
GET /api/ocr/health
响应:
{"status": "UP", "model": "PP-OCRv6_medium_rec + PP-OCRv6_medium_det"}POST /api/ocr/recognize
Content-Type: multipart/form-data
Body: image (File)
响应:
{"text": "3425698840", "confidence": 0.9999981}POST /api/ocr/detect
Content-Type: multipart/form-data
Body: image (File)
响应:
{
"boxes": [
{
"points": [[362,426], [1278,426], [1278,576], [362,576]],
"score": 0.9135921
}
]
}POST /api/ocr/pipeline
Content-Type: multipart/form-data
Body: image (File)
响应:
{
"results": [
{"text": "3425698840", "confidence": 0.9999981}
]
}POST /api/ocr/markdown
Content-Type: multipart/form-data
Body: image (File)
响应:
{
"markdown": "# 文档标题\n\n这是第一段内容...\n\n这是第二段内容..."
}ocr-service/src/main/java/com/ppocrv6/ocr/
├── OcrApplication.java # Spring Boot 入口
├── config/
│ └── OcrConfig.java # Bean 定义(ONNX 资源、环境、预处理器)
├── controller/
│ └── OcrController.java # REST 端点(5 个 API)
├── model/
│ ├── OcrResult.java # 识别结果(text + confidence)
│ ├── TextBox.java # 检测文本框(4 角坐标 + score)
│ └── AnnotatedText.java # 带坐标的识别结果(布局分析用)
├── service/
│ ├── OcrService.java # 识别模型加载 + 推理 + CTC 解码
│ ├── DetService.java # 检测模型加载 + 推理
│ └── OcrPipelineService.java # 流水线编排(检测→裁剪→识别→Markdown)
└── util/
├── ImagePreprocessor.java # 识别预处理(resize_norm_img NCHW)
├── DetPreprocessor.java # 检测预处理(DetResizeForTest + NormalizeImage)
├── DetPostProcessor.java # DBPostProcess(轮廓 + minAreaRect + unclip)
├── ImageUtils.java # 透射变换裁剪 + 图像旋转
├── CharDictLoader.java # 字符字典加载
├── LayoutAnalyzer.java # 布局分析(分栏 + 段落合并 + 标题检测)
└── MarkdownConverter.java # Markdown 格式化输出
- 与 PaddleOCR 对齐 — 每个预处理/后处理步骤严格匹配 PaddleOCR Python 源码
- 零外部计算机视觉依赖 — 检测后处理(轮廓追踪、凸包、旋转卡壳、多边形扩展、透视变换)全部使用纯 Java 实现,无需 OpenCV/BoofCV
- 向后兼容 — 新增端点不影响已有 API
- 无状态设计 — 服务不保存用户数据,适合容器化部署
| 算法 | 文件 | 说明 |
|---|---|---|
| Moore-Neighbor 轮廓追踪 | DetPostProcessor | 8-连通边界跟踪 |
| Andrew's Monotone Chain 凸包 | DetPostProcessor | 计算点集凸包 |
| Rotating Calipers 最小外接矩形 | DetPostProcessor | 旋转卡壳求最小面积包围盒 |
| Ramer-Douglas-Peucker 轮廓简化 | DetPostProcessor | 简化轮廓折线 |
| DLT 透射变换 | ImageUtils | 直接线性变换 + 高斯消元解 8x8 方程 |
| Scanline 多边形填充 | DetPostProcessor | 扫描线填充算法计算多边形内均值 |
| XY-Cut 分栏检测 | LayoutAnalyzer | X 轴投影分析检测多栏布局 |
| CTC 解码 | OcrService | Argmax → 去重 → 去空白 → 字典映射 |
- JDK: 17+
- Maven: 3.6+
- 内存: ≥ 4GB(两个 ONNX 模型合计约 134MB,推理时需额外内存)
- 无外部服务依赖(无需数据库、缓存、消息队列)
模型文件较大(合计约 134MB),未包含在 Git 仓库中。请手动下载并放置到对应目录:
# 1. 下载识别模型
# 从 https://www.modelscope.cn/models/PaddlePaddle/PP-OCRv6_medium_rec_onnx/files
# 下载所有文件,解压到:
mkdir -p PP-OCRv6_medium_rec_onnx
# 将 inference.onnx 和 char_dict.json 放入 PP-OCRv6_medium_rec_onnx/
# 2. 下载检测模型
# 从 https://www.modelscope.cn/models/PaddlePaddle/PP-OCRv6_medium_det_onnx/files
# 下载所有文件,解压到:
mkdir -p PP-OCRv6_medium_det_onnx
# 将 inference.onnx 放入 PP-OCRv6_medium_det_onnx/
# 3. 复制模型文件到服务资源目录
cp PP-OCRv6_medium_rec_onnx/inference.onnx ocr-service/src/main/resources/
cp PP-OCRv6_medium_rec_onnx/char_dict.json ocr-service/src/main/resources/
cp PP-OCRv6_medium_det_onnx/inference.onnx ocr-service/src/main/resources/inference_det.onnx# 1. 启动服务
cd ocr-service && mvn spring-boot:run
# 2. 测试健康检查
curl http://localhost:8080/api/ocr/health
# 3. 测试完整流水线(用你的图片替换 test.png)
curl -X POST http://localhost:8080/api/ocr/pipeline \
-F "image=@/path/to/your/image.png"
# 4. 测试 Markdown 输出
curl -X POST http://localhost:8080/api/ocr/markdown \
-F "image=@/path/to/your/image.png"cd ocr-service && mvn test目前包含 8 个集成测试,覆盖:
- 模型加载和 Spring 容器初始化
- 识别预处理(张量形状、空值、灰度图)
- 识别端到端(合成文本图片 → 识别)
- 边界情况(空文件、无效数据)
Apache 2.0