# lw.-ppocr.-java.api **Repository Path**: wuyuan/lw.-ppocr.-java.api ## Basic Information - **Project Name**: lw.-ppocr.-java.api - **Description**: 基于 Spring Boot 4.1.1 + lw.PPOCR.Java 0.1.0(纯 Java PP-OCRv6 Tiny 推理运行时)的图片 OCR HTTP 服务 - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-14 - **Last Updated**: 2026-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ocr-api 基于 **Spring Boot 4.1.1** + **lw.PPOCR.Java 0.1.0**(纯 Java PP-OCRv6 Tiny 推理运行时)的图片 OCR HTTP 服务。 上传图片 → 返回 `OcrResult` JSON(全文 + 逐行文本/置信度/检测框坐标)。 ## 环境要求 - **JDK 25**(Vector API 后端硬性要求;本机已装 JDK 25 LTS) - Maven 3.9+ - 纯 CPU,无需 Python / Paddle / ONNX Runtime / OpenCV / JNI ## 依赖安装(已完成一次,重装机器后需重做) 3 个 JAR 已安装到本地 Maven 仓库 `~/.m2/repository/io/github/lxw112190/`: ```powershell # JAR 内嵌 POM 声明了 lw-ppocr-parent 父 POM(发布包未包含),直接用 install-file 会导致 # 解析依赖时报 "Could not find artifact lw-ppocr-parent"。因此必须用独立最小 POM 安装: mvn install:install-file "-Dfile=<下载目录>\lib\lw-ppocr-core-0.1.0.jar" "-DpomFile=local-poms\lw-ppocr-core.pom" mvn install:install-file "-Dfile=<下载目录>\lib\lw-ppocr-imageio-0.1.0.jar" "-DpomFile=local-poms\lw-ppocr-imageio.pom" mvn install:install-file "-Dfile=<下载目录>\lib\lw-ppocr-vector-0.1.0.jar" "-DpomFile=local-poms\lw-ppocr-vector.pom" ``` `local-poms/` 目录已放在本项目中,可直接复用。若已用内嵌 POM 装过,把 `local-poms` 里对应 `.pom` 覆盖到 `~/.m2/repository/io/github/lxw112190//0.1.0/` 下的 `.pom` 文件即可修复。 > 项目**默认启用 Vector API 加速后端**(`ocr.backend: vector`)。如遇兼容性问题可在 > `application.yml` 改为 `ocr.backend: scalar` 回退到正确性参考路径(此时无需 incubator 模块)。 ## 启动 ```powershell cd ocr-api # 方式一:Maven 启动(pom 已配置 jvmArguments,自动带 --add-modules jdk.incubator.vector) mvn spring-boot:run # 方式二:打包后运行(必须手动带 incubator 模块参数) mvn package .\run.ps1 # 等价于:java --add-modules jdk.incubator.vector -jar target\ocr-api-0.0.1-SNAPSHOT.jar ``` 服务默认端口 **8080**(`application.yml` 可改)。启动日志出现 `推理后端:VectorBackend(JDK 25 Vector API 加速)` 即表示向量化内核已生效。 ## 接口 ### 1. 图片识别 ```http POST /api/ocr/upload Content-Type: multipart/form-data 参数:file = <图片文件>(jpg/png 等,≤10MB) ``` curl 示例: ```powershell curl -X POST -F "file=@sample.jpg" http://localhost:8080/api/ocr/upload ``` 返回 `200` + `OcrResult` JSON: ```json { "lines": [ { "box": { "points": [59.0, 34.0, 351.0, 34.0, 351.0, 66.0, 59.0, 66.0], "score": 0.982 }, "text": "纯Java的PP-OCR", "recognitionScore": 0.987, "classification": { "label": 0, "score": 0.99, "orientationDegrees": 0, "resizedWidth": 320 }, "rotated": false } ], "text": "纯Java的PP-OCR" } ``` 字段说明: | 字段 | 含义 | | --- | --- | | `text` | 整页全文(多行以 `\n` 连接) | | `lines[]` | 按阅读顺序排列的文本行 | | `lines[].text` | 该行识别文本 | | `lines[].recognitionScore` | 该行识别置信度 0~1 | | `lines[].box.points` | 检测框四点坐标 `[x1,y1,x2,y2,x3,y3,x4,y4]`(顺时针) | | `lines[].box.score` | 检测框置信度 | | `lines[].classification` | CLS 方向分类结果(未启用时 `null`) | | `lines[].rotated` | 该行是否做了 180° 旋转校正 | ### 2. 健康检查 ```http GET /api/ocr/health ``` ## 配置(application.yml) | 配置项 | 默认 | 说明 | | --- | --- | --- | | `ocr.models-dir` | `./models` | 模型目录(含 det/cls/rec .lwm + ppocr_keys.txt) | | `ocr.backend` | `vector` | 推理后端:`vector`=JDK 25 Vector API 加速;`scalar`=正确性参考路径 | | `ocr.warmup` | `true` | 启动时用 sample.jpg 预热 2 次,吸收首次 JIT 编译成本 | | `ocr.warmup-async` | `false` | 预热异步执行:true=启动 ~5s 即 ready,但前 1-2 个请求承担 JIT 编译(~7.5s) | | `ocr.workers` | `4` | 并发 OCR 实例数(OcrWorkerPool 池大小) | | `ocr.detection-max-side` | `320` | 检测最大边长:320 低延迟 / 960 高精度 | | `ocr.classification-parallelism` | `4` | CLS 方向分类并行度 | | `ocr.recognition-parallelism` | `4` | REC 识别并行度 | 模型缺失 `cls.lwm` 时自动禁用方向分类,不影响启动。 ## 性能实测(i5-9300H / JDK 25 / sample.jpg,det=320) | 后端 | 首次请求 | 预热后稳定值 | | --- | --- | --- | | `scalar` | ~10.4s(JIT 预热) | ~5.5~6s | | `vector` | ~10.3s(JIT 预热) | **~0.6s** | - Vector 预热后比 Scalar **快约 9 倍**;首次 ~10s 是 JIT 编译向量内核的一次性成本。 - 项目已内置**启动预热**(`ocr.warmup=true`):启动日志会输出 `启动预热 第 x/2 次:识别 16 行,耗时 xxx ms`,把 ~14s 编译成本转移到启动阶段, 线上第一个请求即 ~2s、随后稳定 ~0.6s。 - 若用 IDEA **调试模式**启动(带 jdwp 断点),首次推理会明显更慢,误以为"卡死"; 建议生产/压测用 `.\run.ps1` 或 `mvn spring-boot:run` 启动,不要在断点下压测。 ### 同步预热 vs 异步预热 | 模式 | 启动就绪 | 首个外部请求 | 稳定值 | 适用场景 | | --- | --- | --- | --- | --- | | 无预热(`warmup=false`) | ~4s | ~10.3s | ~0.6s | 不在乎首请求延迟的本地调试 | | 同步预热(默认) | **~20s** | **~2.3s** | ~0.6s | 线上首选:首请求体验最好 | | 异步预热(`warmup-async=true`) | **~5.5s** | ~7.5s | ~0.6s | 追求快速就绪接流量的场景 | 异步预热 = 启动快 + 首请求慢:JIT 编译在后台线程继续,前 1-2 个请求会与预热竞争 CPU 并承担剩余编译成本(实测 7.5s)。若用异步预热,建议配合就绪探针:等日志出现 "预热完成"再放流量(效果等价于同步预热);对 K8s 可把 readiness 绑定到预热完成标志。 ## 项目结构 ```text ocr-api/ ├─ pom.xml # Spring Boot 4.1.1 + lw-ppocr(core/imageio/vector) 依赖 ├─ run.ps1 # 一键启动(自动带 Vector API 孵化模块参数) ├─ models/ # PP-OCRv6 Tiny LWM 模型(已从下载包复制) │ ├─ det.lwm cls.lwm rec.lwm ppocr_keys.txt └─ src/main/ ├─ java/com/example/ocrapi/ │ ├─ OcrApiApplication.java │ ├─ config/ # 配置属性 + OcrWorkerPool Bean(按 ocr.backend 选后端) │ ├─ controller/ # POST /api/ocr/upload │ └─ web/ # 全局异常处理 └─ resources/application.yml ``` ## 关键实现说明 - **并发安全**:`PaddleOcr` 单实例非线程安全;启动时按 `ocr.workers` 创建 N 个独立实例放入 `OcrWorkerPool`(内部排队分配、线程安全),并发请求不会串扰。 - **图片解码**:`ImageIoLoader.load(InputStream)` → `BgrImage` → `pool.recognize(BgrImage)`。 - **资源释放**:应用关闭时 `OcrWorkerPool.close()` 会等待在途请求完成并关闭全部 Session。 - **Vector API 加速**:`OcrConfig` 按 `ocr.backend` 创建 `VectorBackend`(JDK 25 孵化 Vector API), 加速 Tiny 模型全部 Conv / DET 2× ConvTranspose / MatMul / 归约 / 激活 / 广播,并融合五节点 `DIV→ERF→ADD→MUL→MUL` GELU;优化范围外的通用形状自动回退 Scalar,不影响正确性。 - **编译与运行要求**:`pom.xml` 已配置 `maven-compiler-plugin` 编译期 `--add-modules jdk.incubator.vector`、`spring-boot-maven-plugin` 运行期同参数; `java -jar` 场景请使用 `run.ps1`。 ## 已知边界(lw.PPOCR.Java 0.1.0) - 仅验证动态形状 FP32 PP-OCRv6 **Tiny** 模型集;不支持任意 ONNX 拓扑、GPU、Android。 - 模型为 LWM v0.1 格式,由 `lw.PPOCR.C` 离线转换,本仓库不解析 ONNX。 - Vector 后端仅在 JDK 25 + `--add-modules jdk.incubator.vector` 下生效;改 `ocr.backend: scalar` 可回退。