# doclayout-java **Repository Path**: wuyuan/doclayout-java ## Basic Information - **Project Name**: doclayout-java - **Description**: 把 PaddlePaddle 官方 PP-DocLayoutV3 ONNX 模型 - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-20 - **Last Updated**: 2026-09-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PP-DocLayoutV3 Java 集成(doclayout-java) 把 PaddlePaddle 官方 **PP-DocLayoutV3** ONNX 模型([HuggingFace: PaddlePaddle/PP-DocLayoutV3_onnx](https://huggingface.co/PaddlePaddle/PP-DocLayoutV3_onnx))集成进 Java: - **doclayout-core**:核心模块(ONNX Runtime 推理 + OpenCV 预处理 + 对齐 PaddleX 的后处理),参考 [dreamlu/mica-ai · mica-ai-layout](https://gitee.com/dreamlu/mica-ai/tree/master/mica-ai-core/mica-ai-layout) 的模块设计,零 Spring 依赖、可独立复用; - **doclayout-server**:Spring Boot **4.1.1** 提供的 REST API; - **compare/**:与 Python(PaddleX/paddleocr 官方 onnxruntime 流水线)的逐值对比。 > ⚠️ 用户指定的仓库 `PaddlePaddle/PP-DocLayoutV3_onnx_onnx` 在 HuggingFace 上不存在(404), > 官方实际仓库为 **`PaddlePaddle/PP-DocLayoutV3_onnx`**,内含 `inference.onnx`(130 MB,3 输入 3 输出),本项目使用该官方模型。 --- ## 1. 工程结构 ``` doclayout-java/ ├── pom.xml # 父 POM(聚合 core + server) ├── doclayout-core/ │ └── src/main/java/io/doclayout/ │ ├── config/LayoutConfig.java # 配置(阈值/NMS 等,默认对齐 PaddleX) │ ├── model/LayoutLabel.java # 25 类标签 + 跳过阅读顺序名单(11 类) │ ├── model/LayoutResult.java # 推理结果 │ ├── detection/LayoutDetector.java # ONNX Runtime 推理(输入节点按名称提示解析) │ ├── postprocess/LayoutPostProcessor.java # 后处理(完整复刻 PaddleX) │ ├── pipeline/LayoutPipeline.java # 门面:detectPath / detectBytes / detect(Mat) │ └── util/LayoutImageUtils.java # 图像工具(org.openpnp:opencv) ├── doclayout-server/ │ ├── pom.xml │ └── src/main/ │ ├── java/io/doclayout/server/ │ │ ├── DocLayoutServerApplication.java │ │ └── controller/LayoutController.java # POST /api/layout/detect 等 │ └── resources/application.yml └── compare/ # Python 复刻脚本 + 对比脚本 + 结果明细 ``` ## 2. 黄金流水线(与 PaddleX 官方 onnxruntime 引擎逐值对齐) 模型 I/O(官方导出形态): | 方向 | 名称 | 形状 / 类型 | 说明 | |---|---|---|---| | 输入 | `image` | `[N,3,800,800]` float32 | RGB + CHW + ×(1/255) | | 输入 | `im_shape` | `[N,2]` float32 | `[800, 800]` | | 输入 | `scale_factor` | `[N,2]` float32 | `[800/orig_h, 800/orig_w]` | | 输出 | `fetch_name_0` | `[N,7]` float32 | `[cls, score, x1, y1, x2, y2, order]`,坐标**已是原图尺度** | | 输出 | `fetch_name_1` | `[N]` int32 | 有效数(未消费) | | 输出 | `fetch_name_2` | `[N,200,200]` int32 | 指针网络(未消费) | 预处理(读图 → 输出张量): 1. 解码图像(BGR)→ `cvtColor(BGR2RGB)` 2. `resize(800×800, INTER_CUBIC)` —— 官方 yml `keep_ratio: false`,**直接拉伸**(非 letterbox) 3. 像素 × `1/255`(官方 yml `norm_type: none` + mean 0 + std 1,无 mean/std 归一化) 4. HWC → CHW > 本工程直接使用官方 HF 模型,实测(paddleocr onnxruntime 截获输入张量)官方流水线为**拉伸 + 1/255**,据此实现。 后处理(`LayoutPostProcessor`,完整复刻 `paddlex/.../layout_analysis/processors.py`): 1. `round(boxes[:,2:6])`(np.round = round-half-to-even → Java `Math.rint`) 2. 阈值过滤 `score > 0.5`(严格大于,对齐 `draw_threshold`) 3. 贪心 NMS:score 降序,同类 IoU>0.6 / 异类 IoU>0.98 互斥(`iou < thr` 保留) 4. 整页 `image` 伪框过滤(面积占比 > 0.82 横版 / 0.93 竖版丢弃;全丢弃回退保留) 5. 按第 7 列 `order` 键升序排序(对齐 `np.argsort`) 6. clip 到原图 + 丢弃无效框(`restructured_boxes`) 7. `filter_boxes`:剔除 `reference`、宽或高 <6 的框、重叠框(small-overlap>0.7 大框吃小框;`inline_formula` 特殊规则;`image/table/seal/chart` 跨类豁免) 8. `update_order_index`:跳过类(11 类)order=null 不占号,其余从 1 编号 ## 3. API(Spring Boot 4.1.1) 启动: ```bash cd doclayout-server java -jar target/doclayout-server-1.0.0.jar --server.port=8080 ``` | 端点 | 方法 | 请求 | 响应 | |---|---|---|---| | `/api/layout/detect` | POST | `multipart/form-data`,字段 `file` | `[{label, labelCode, score, boundingBox, order}, ...]` | | `/api/layout/detect-bytes` | POST | `application/octet-stream` 原始字节 | 同上 | 示例: ```bash curl -X POST http://localhost:8080/api/layout/detect -F "file=@8.jpg" curl -X POST http://localhost:8080/api/layout/detect-draw -F "file=@8.jpg" -o result.png ``` 配置(`application.yml`): ```yaml doclayout: model-path: /path/to/inference.onnx # 支持绝对/相对路径或 classpath: score-threshold: 0.40 ``` ## 4. 与 Python 对比 ### 方法 - **Python 基准**:复刻 PaddleX 官方流水线(`compare/replicate_paddlex.py`),该脚本的预处理/后处理 已通过**截获 paddleocr 3.7.0 onnxruntime 引擎的输入张量与原始输出**逐值验证(`image` 张量、`im_shape`、 `scale_factor`、`fetch_name_0` 均一致),且最终 13 个框与 `paddleocr LayoutDetection` 输出逐值一致。 - **Java 端**:`doclayout-core` 推理 + `doclayout-server` API。 - **对比**:`compare/compare_java_python.py` 逐字段(label、score、boundingBox 4 坐标、order)自动比对。 ### 结果(2026-09-20) | 测试图 | 尺寸 | Python 框数 | Java 框数 | label | score | bbox | order | 结论 | |---|---|---|---|---|---|---|---|---| | demo_doc.jpg(竖版双栏论文) | 1654×2339 | 13 | 13 | ✅ | ✅(float32 逐位一致) | ✅ 逐坐标一致 | ✅ | **完全一致** | | pp_structure_v3_demo.png(横版) | 1524×1368 | 31 | 31 | ✅ | ✅(float32 逐位一致) | ✅ 逐坐标一致 | ✅ | **完全一致** | 共 **44 个版面框**,Java 与 Python 输出逐字段一致(score 为 float32 原始值,JSON 序列化显示 6 位小数)。 ### 明细文件 - `compare/python_demo_doc.json` / `compare/java_demo_doc.json` - `compare/python_structure.json` / `compare/java_structure.json` - `compare/replicate_paddlex.py`(Python 黄金流水线,可复现) - `compare/compare_java_python.py`(自动对比脚本,可复现) ## 5. 构建 ```bash export JAVA_HOME=/path/to/jdk17 mvn -B package -DskipTests ``` - Java 17+(Spring Boot 4.1.1 要求) - 依赖:`com.microsoft.onnxruntime:onnxruntime:1.20.0`(与 Python onnxruntime 同版本,保证数值一致)、`org.openpnp:opencv:4.9.0-0`、Spring Boot 4.1.1 ## 6. 模型获取 ```bash python3 -c " from huggingface_hub import hf_hub_download hf_hub_download('PaddlePaddle/PP-DocLayoutV3_onnx', 'inference.onnx', local_dir='models/PP-DocLayoutV3_onnx') " ```